続：React Reduxを簡単に利用してみる


[前回](https://qiita.com/zaburo/items/6f9158213a0deb92ae7b)のコードにAPI照会機能を追加したいと思います。
非同期処理の記述はsagaと悩みましたが、ひとまずthunkを利用しました（既存コードの理解も考慮）。また通信にはaxiosを利用します。

## 準備

axiosとthunkをインストールします。

```bash
npm install --save axios
npm install --save redux-thunk
```

## 実装

Thunkを利用するためのactions, reducer関連の記述を追加します。

### actions

通信処理用の３つのaction(creater)を定義します。

* 開始時：FETCH_MESSAGES(requestMessages)
* 成功時：FETCH_MESSAGES_SUCCESS(recieveMessages)
* 失敗時：FETCH_MESSAGES_ERROR(recieveMessages)

それと、それらを実際にコールするfetchMessages()を定義します。

### reducers

新規にデータ処理関連を取り扱うreducerを定義し、combineReducers()します。

### middlewareの追加

最後にというわけではないですが、middleware利用の定義をcreateStore()に追加します。

```js: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を定義して行います。


```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を指定してるだけ）。

```js: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し、利用できるようにしています。

```js
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
<?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);
```