前回のコードにAPI照会機能を追加したいと思います。
非同期処理の記述はsagaと悩みましたが、ひとまずthunkを利用しました(既存コードの理解も考慮)。また通信にはaxiosを利用します。
準備
axiosとthunkをインストールします。
npm install --save axios
npm install --save redux-thunk
実装
Thunkを利用するためのactions, reducer関連の記述を追加します。
actions
通信処理用の3つのaction(creater)を定義します。
- 開始時:FETCH_MESSAGES(requestMessages)
- 成功時:FETCH_MESSAGES_SUCCESS(recieveMessages)
- 失敗時:FETCH_MESSAGES_ERROR(recieveMessages)
それと、それらを実際にコールするfetchMessages()を定義します。
reducers
新規にデータ処理関連を取り扱うreducerを定義し、combineReducers()します。
middlewareの追加
最後にというわけではないですが、middleware利用の定義をcreateStore()に追加します。
redux.js
import {combineReducers, createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk'
import axios from 'axios'
//actions.js
//user関連
export const editName = _name => ({
type: "EDIT_NAME",
name: _name
});
export const deleteName = () => ({
type: "DELETE_NAME",
name: ''
});
//api連携関連
export const requestMessages = () => ({
type: "FETCH_MESSAGES"
});
export const recieveMessages = json => ({
type: "FETCH_MESSAGES_SUCCESS",
data: json
});
export const errorMessages = () => ({
type: "FETCH_MESSAGES_ERROR",
});
//リクエストの本体(この中で上記action createrを使う)
export const fetchMessages = () => (
dispatch => {
//start
dispatch(requestMessages());
return axios.get('http://localhost/').then((res)=>{
//end
dispatch(recieveMessages(res.data));
}).catch((error) => {
dispatch(errorMessages());
});
}
)
//reducers.js
//reducerの_userと命名
export const _user = (state = {}, action) => {
switch (action.type){
case 'EDIT_NAME':
return action.name;
case 'DELETE_NAME':
return action.name;
default:
return state;
}
}
//reducers(data関連)
export const _data = (state = {}, action) => {
switch(action.type){
case 'FETCH_MESSAGES':
return Object.assign({}, state, {
isFetching: true
});
case 'FETCH_MESSAGES_SUCCESS':
return Object.assign({}, state, {
isFetching: false,
data: action.data
});
case 'FETCH_MESSAGES_ERROR':
return Object.assign({}, state, {
isFetching: false,
message: 'error'
});
default:
return state;
}
}
//keyを指定して別名に(keyを指定しないと_userとなる) state.userで参照できる。
export const reducers = combineReducers({
user: _user,
data: _data
});
//値は以下の構造となる。
const initialState = {
user: {
name: 'foo'
},
data: {
data: {
status: 'N/A',
data: []
}
}
};
//store.js
export const store = createStore(reducers,initialState, applyMiddleware(thunkMiddleware));
App.jsを記述します。データを表示するための<DataList/>を追加しました。なお、<DataList>の実装は別途DataList.jsを定義して行います。
App.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import { editName, deleteName, fetchMessages } from './redux'
import DataList from './DataList'
class App extends Component {
render() {
return (
<div className="App">
{/* USER.name等で参照可 */}
<h3>{this.props.USER.name}</h3>
<button onClick={() => this.props.editName({name: 'my name is hoge.'})}>Add</button>
<button onClick={() => this.props.deleteName()}>Del</button>
<button onClick={() => this.props.fetchMessages()}>Fetch</button>
<h3>{this.props.DATA.data.status}</h3>
{/* <DataList {...props}/> */}
<DataList/>
</div>
);
}
}
//combineで付けたkey名でマッピング(props.USERにマップ)
const mapStateToProps = state => ({
USER: state.user,
DATA: state.data
});
//dispatchはそのまま
//props.addName()にマップ
const mapDispatchToProps = {
editName,deleteName,fetchMessages
}
//バインドさえれたAppが返る
export default connect(mapStateToProps,mapDispatchToProps)(App)
// export default App;
index.jsに変更はありません(Providerで囲ってstoreを指定してるだけ)。
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux'
import { store } from './redux'
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('root')
);
DataList.jsファイルを用意し記述します。
storeにconnectし、利用できるようにしています。
import React, { Component } from 'react';
import { connect } from 'react-redux'
// import { editName, deleteName, fetchMessages } from './redux'
class DataList extends Component {
render() {
const items = this.props.DATA.data.data.map(item => {
return (
<li key={item.id}>{item.id} {item.name} {item.age}</li>
);
});
return (
<ul>
{items}
</ul>
);
}
}
//combineで付けたkey名でマッピング(props.USERにマップ)
const mapStateToProps = state => ({
DATA: state.data
});
//dispatchはそのまま
//props.addName()にマップ
const mapDispatchToProps = {
}
//バインドさえれたAppが返る
export default connect(mapStateToProps, mapDispatchToProps)(DataList)
API側のソース。
<?php
$res['status'] = 'OK';
$res['data'] = [
["id"=>1,"name"=>"yamada","age"=>33],
["id"=>2,"name"=>"tanaka","age"=>44],
["id"=>3,"name"=>"suzuki","age"=>55]
];
header('Access-Control-Allow-Origin:*');
echo json_encode($res);