React、Reduxの初心者向け記事です。
axiosを使ってAPIを叩き、その結果をReactで表示させたいと思います。
今回はこちらのサービスを使用させていただきました。
郵便番号-住所検索API
ミドルウェアの導入
Reduxで非同期処理をするためにはミドルウェアが必要となります。
ミドルウェアはredux-thunkとredux-sagaが有名なようです。
redux-thunkは非同期処理をAction内で行い、redux-sagaはSagaという独立したプロセス内で行うという違いがありますが、今回は導入が簡単そうなredux-thunkを採用しました。
まずはredux-thunkをインストールします。
$ npm install redux-thunk
storeを作成している場所でredux-thunkを導入します。
createStore()にapplyMiddleware()を設定することでミドルウェアを導入することができます。
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で非同期処理のリクエストを投げています。
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を実装します。
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の部分を実装します。
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);
7桁の数字を入力した瞬間に住所を表示したかったので e.target.value
の値を直接getAddress にdispatchしています。
入力した瞬間はまだthis.props.data
がundefinedなためdispatchした直後はsetStateできません。
なのでcomponentWillReceiveProps
を使用して、propsがなにかしら変化した直後にsetStateしています。
React.js のライフサイクルメソッド componentWillReceiveProps の廃止対応
componentWillReceiveProps
はReactのv17以降廃止されるらしいので今後はUNSAFE_componentWillReceiveProps
に書き換えるか、v16.3から新しく導入されたgetDerivedStateFromProps
に置き換える必要があるようです…
参考
React + ReduxでREST APIを叩いてリスト表示する方法
reduxで非同期処理をするいくつかの方法(redux-thunk、redux-saga)
React.js のライフサイクルメソッド componentWillReceiveProps の廃止対応