React、Reduxとaxiosを使って郵便番号から住所を取得する

React、Reduxの初心者向け記事です。
axiosを使ってAPIを叩き、その結果をReactで表示させたいと思います。

今回はこちらのサービスを使用させていただきました。
[郵便番号-住所検索API](https://zipaddress.net/)

## ミドルウェアの導入
Reduxで非同期処理をするためにはミドルウェアが必要となります。
ミドルウェアは**redux-thunk**と**redux-saga**が有名なようです。
redux-thunkは非同期処理をAction内で行い、redux-sagaはSagaという独立したプロセス内で行うという違いがありますが、今回は導入が簡単そうなredux-thunkを採用しました。

まずはredux-thunkをインストールします。

```
$ npm install redux-thunk
```
storeを作成している場所でredux-thunkを導入します。
createStore()にapplyMiddleware()を設定することでミドルウェアを導入することができます。

```React:index.js
import React from 'react';
import ReactDOM from 'react-dom';
import thunk from 'redux-thunk';
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux';
import './index.css';
import reducer from './reducers';
import Routes from './containers/Routes';

const store = createStore(
  reducer,
  applyMiddleware(thunk)
);

ReactDOM.render(
  <Provider store={store}>
    <Routes />
  </Provider>,
  document.getElementById('root'));
```

## Actionを実装
次にActionを書いていきます。
最初にActionのタイプを作成し、ActionCreatorで非同期処理のリクエストを投げています。

```React:Action.js
import axios from 'axios'
export const GET_ADDRESS_REQUEST = 'GET_ADDRESS_REQUEST'
export const GET_ADDRESS_SUCCESS = 'GET_ADDRESS_SUCCESS'
export const GET_ADDRESS_FAILURE = 'GET_ADDRESS_FAILURE'

export const getAddressRequest = () => {
  return {
    type: GET_ADDRESS_REQUEST
  }
}

export const getAddressSuccess = (data) => {
  return {
    type: GET_ADDRESS_SUCCESS,
    data
  }
}

export const getAddressFailure = (error) => {
  return {
    type: GET_ADDRESS_FAILURE,
    error
  }
}

export const getAddress = (zipcode) => {
  return async (dispatch) => {
    dispatch(getAddressRequest(zipcode))
    try {
      const res = await axios.get('https://api.zipaddress.net/?', { params: { zipcode: zipcode } });
      return dispatch(getAddressSuccess(res.data));
    }
    catch (err) {
      return dispatch(getAddressFailure(err));
    }
  }
}

```
## Reducerを実装
先程作成したActionをつかってReducerを実装します。

```React:Reducer.js
import {
  GET_ADDRESS_REQUEST, GET_ADDRESS_SUCCESS, GET_ADDRESS_FAILURE
} from '../actions/Action'

const initalState = {
  isFetching: false,
  address: []
}

const getAddress = (state = initalState, action) => {
  switch (action.type) {
    case GET_ADDRESS_REQUEST:
      return Object.assign({}, state, {
        isFetching: true,
        address: []
      });
    case GET_ADDRESS_SUCCESS:
      return Object.assign({}, state, {
        isFetching: false,
        address: action.data,
      });
    case GET_ADDRESS_FAILURE:
      return Object.assign({}, state, {
        isFetching: false,
        error: action.error
      });
    default:
      return state
  }
}

export default getAddress
```
## コンポーネントを編集
最後にviewの部分を実装します。

```React:View.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getAddress } from '../actions/Action'

class View extends Component {

  constructor(props){
    super(props);
    this.state = {
      text: '',
      address: ''
    }

    this.handleChange = this.handleChange.bind(this)
  }

  handleChange(e){
    if (e.target.value.length === 7) {
      this.props.getAddress(e.target.value)
    }
    this.setState({
      text: e.target.value
    })
  }

  componentWillReceiveProps(nextProps){
    if(nextProps.data){
      this.setState({
        address: nextProps.data.fullAddress
      })
    }
  }

  render() {
    return (
      <div>
        郵便番号から住所を表示します
        <br/>
        <input type='text' value={this.state.text} onChange={this.handleChange}></input>
        <p>入力した郵便番号：{this.state.text}</p>
        <p>住所： {this.state.address}</p>
      </div>
    );
  }
}

const mapStateToProps = (state) => ({
  data: state.getAddress.address.data
});

function mapDispatchToProps(dispatch) {
  return {
    getAddress(zipcode){
      dispatch(getAddress(zipcode))
    }
  };
}

export default connect(mapStateToProps, mapDispatchToProps )(View);

```

<img width="340" alt="スクリーンショット 2019-06-21 17.09.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439548/22bf5217-4288-6727-9598-f6594a00fd29.png">

7桁の数字を入力した瞬間に住所を表示したかったので `e.target.value`の値を直接getAddress にdispatchしています。
入力した瞬間はまだ`this.props.data`がundefinedなためdispatchした直後はsetStateできません。
なので`componentWillReceiveProps`を使用して、propsがなにかしら変化した直後にsetStateしています。

[React.js のライフサイクルメソッド componentWillReceiveProps の廃止対応](https://qiita.com/mori-dev@github/items/c0a458384b5390494784)

`componentWillReceiveProps`はReactのv17以降廃止されるらしいので今後は`UNSAFE_componentWillReceiveProps`に書き換えるか、v16.3から新しく導入された`getDerivedStateFromProps`に置き換える必要があるようです…

## 参考
[React + ReduxでREST APIを叩いてリスト表示する方法](https://qiita.com/kazmaw/items/a2def8978127ffb11f92)
[reduxで非同期処理をするいくつかの方法（redux-thunk、redux-saga）](https://qiita.com/muiscript/items/63386fd65c7e9f06f5d4)
[React.js のライフサイクルメソッド componentWillReceiveProps の廃止対応](https://qiita.com/mori-dev@github/items/c0a458384b5390494784)