【React Native】簡単なAPI連携のアプリケーションを作成する　②住所検索の実装

# はじめに
本記事は下記の記事の続編になります。
[【React Native】簡単なAPI連携のアプリケーションを作成する　①準備編](https://qiita.com/kazuki_yoshida/items/d5f47e4d39a9e54f091d)
実践的なアプリを作成するというよりは、ハンズオン的にReact Nativeを触ってみるといった趣旨になっています。

## 本記事で説明すること
* axiosの導入・利用
* APIへのリクエスト(住所検索)<br>
Reactで同じようなことをやっている方がいらっしゃいました。<br>
→[React、Reduxとaxiosを使って郵便番号から住所を取得する](https://qiita.com/oda3104/items/317a1309183d50061a6e)

## 環境
* axios：0.21.1

## 今回使用するAPI
* [郵便番号検索API](http://zipcloud.ibsnet.co.jp/doc/api)

## Githubリポジトリ（追記：削除済み）
https://github.com/pbyoshida/postal-app

# 実装
それでは前回のソースから手直しを始めていきます。
## axiosの導入
まずはnpmでaxiosをインストールします。

```powershell
$ npm install axios --save
```
package.jsonを確認します。

```json:package.json
{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject"
  },
  "dependencies": {
    "axios": "^0.21.1",
    "expo": "~40.0.0",
    "expo-status-bar": "~1.0.3",
    "react": "16.13.1",
    "react-dom": "16.13.1",
    "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
    "react-native-web": "~0.13.12"
  },
  "devDependencies": {
    "@babel/core": "~7.9.0"
  },
  "private": true
}

```
11行目にaxiosの記載があり、導入できていそうですね。
ではソースを修正していきます。

```jsx:app.jsx
import React, { useState } from 'react';
import { StyleSheet, Text, View, TextInput, Button, Alert } from 'react-native';
import axios from 'axios';

// 郵便番号検索APIのURL
const zipcloudURL = 'https://zipcloud.ibsnet.co.jp/api/search';

export default function App() {
  // 郵便番号を保存しておくstate
  const [postalCode, setPostalCode] = useState('');
  const [address, setAddress] = useState('');

  // axiosのGETメソッドを使った住所検索
  const fetchAddress = async () => {
    try {
      const response = await axios.get(`${zipcloudURL}?zipcode=${postalCode}`);
      const data = response.data;
      if ( !data.results ) {
        return '該当する住所はありませんでした。'
      }
      switch (data.status) {
        case 200:
          // 今回はテストなので、同じ郵便番号で2件以上存在する場合は除きます
          return `${data.results[0].address1}${data.results[0].address2}${data.results[0].address3}`;
        case 400:
          return data.message;
        case 500:
          return data.message;
      }
    } catch (error) {
       return '検索失敗';
    }
  }

  // 送信ボタンを押した時に実行される関数
  async function handlePress() {
    // 7桁の数字を正規表現で置きます
    const pattern = /^[0-9]{7}$/;
    if (pattern.test(postalCode)){
      const address = await fetchAddress();
      setAddress(address);
    } else {
      // 想定していない文字列の場合
      Alert.alert('正しい郵便番号ではありません', 'もう一度入力してください');
    }
  }

  // アプリに描画する内容
  return (
    <View style={styles.container}>
      <Text style={styles.description}>郵便番号を入力してください{"\n"}(ハイフンなし7桁)</Text>
      <TextInput
        value={postalCode}
        style={styles.inputPostalCode}
        onChangeText={(text) =>{
          setPostalCode(text);
        }}
        maxLength={7}
        keyboardType={"numeric"}
        placeholder={"郵便番号"}
      />
      <Button
        title="送信"
        color="#AAAAAA"
        onPress={handlePress}
      />
      {address.length > 0 && (
        <View style={styles.addressContainer}>
          <View style={styles.addressLabel}>
            <Text style={styles.addressLabelText}>住所</Text>
          </View>
          <View style={styles.address}>
            <Text style={styles.addressText}>{address}</Text>
          </View>
        </View>
      )}
    </View>
  );
}

// 各要素のスタイル
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  description: {
    fontSize: 18,
    color: '#666666',
    paddingBottom: 10,
  },
  inputPostalCode: {
    textAlignVertical: 'center',
    width: 120,
    fontSize: 24,
    marginBottom: 10,
    paddingLeft: 5,
    borderWidth: 1,
    borderColor: 'rgba(0, 0, 0, 0.2)',
  },
  addressContainer: {
    paddingVertical: 10,
    width: 280,
  },
  addressLabel: {
    paddingBottom: 10,
  },
  addressLabelText: {
    fontSize: 18,
    color: '#666666',
  },
  address: {
    paddingLeft: 5,
  },
  addressText: {
    fontSize: 16,
    color: '#000000',
  },
});

```

結構ソースが長くなってしまっています。今回はQiita用に長めに書いているのもありますが、
どの程度分割すべきかあまり分かっていない節はあります。

## 中身の解説
fetchAddressメソッドがaxiosを用いてAPIにリスエストする処理です。
通常は連結した住所の文字列をreturnします。
検索結果が0件の場合、data.resultsがnullになるのでその場合は0件のメッセージをreturnします。

また、送信ボタンを押したときの処理として、7桁の数字かを確認し、
変な文字列を入力していた場合にはアラートでユーザーに通知するようにしています。

レンダリングする処理についてはaddressの長さで判断し、検索後は住所を表示します。

## 動作確認
* 初期画面

<img width="400" alt="1初期画面.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289995/16d31658-e0b2-8474-ce15-2b77510bd335.png">

* 正常に検索結果を表示

<img width="400" alt="2正常系.png]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289995/937b1461-0a79-7d25-726c-7fac37109c62.png">

* 数字以外の文字列の場合

<img width="400" alt="3不正な文字列.png]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289995/a74b8fa3-c30b-2436-2619-7883d35affd9.png">
　　　　　　　↓　送信ボタン押下
<img width="400" alt="4アラート.png]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289995/0fc141df-0068-0944-2ff9-4c024c3c157c.png">

* 検索結果が0件の場合

<img width="400" alt="5存在しない郵便番号.png]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289995/e9e65a27-241d-71b2-e509-9b1b2fc5bedd.png">

## 次回予告
あと1,2回で完結予定です。
ただ、次回はちょっと寄り道をしてESLint+Prettierの導入をしようと思います。
(今回ソースを書いててなかなか不便だったのと、備忘を兼ねて。。)

2021/2/20 続きを書きました。
→[【React Native】簡単なAPI連携のアプリケーションを作成する　③ESLint+Prettierの導入(寄り道)](https://qiita.com/kazuki_yoshida/items/4365b69cf80f1f65692b)

2021/2/21 最終回を書きました。
→[【React Native】簡単なAPI連携のアプリケーションを作成する　④天気情報表示の実装](https://qiita.com/kazuki_yoshida/items/f6f576ec1782b8878e6f)