データの準備
データはjsonファイルとして準備します。前回のプロジェクトのルートにフォルダを作成し, データを置いておきます。 このデータをwebpack-dev-serverがなんとかしてくれます。first (プロジェクト名) |- data |- data.json
data.json
{ "data" : [ { "index" : 1, "name" : "Taro" }, { "index" : 2, "name" : "Jiro" } ] }これで, localhost:8080/data/data.json でデータを取得できるようになりました。
HttpClientはどうする?
React そのものには, HttpClientは含まれていません。ですので何かしらのライブラリの導入をするか, pure Javascript で頑張るかのどちらかになります。 選択肢としてはいくつかあります。- jQuery
- Angular
- SuperAgent
- Axios
- Request
- Fetch
npm insatll whatwg-fetch --save
React-ThunkとMiddleware
今回のサンプルを実装するに当たって重要なのが React-Thunkと Middlewareです。 Redux Thunk (Thunk middleware for Redux.) これを使うことでAction CreatorがAction objectを返す代わりにfunctionを返すことができます。npm install redux-thunk --save
非同期データとアクション
ここで非同期でデータを取ってくるところとアクションの関係を説明します。 データを取ってくる = データを取りに行く + データを受信した or データ取得に失敗した という形に分離できます。これでアクションは3種類? 実装する必要がありますね。 (結局このあたりの細かいところをどう設計するかが鍵になるのかと思います。最初のうちはさっぱりわからないですよね。)サンプル
構成は前回のエントリーの続きになります。 webpackの設定などの変更はしていないです。first |-data | |- data.json |-.babelrc |-index.html |-package.json |-webpack.config.js |-src |-actions | |- index.jsx |-components | |- FormDisplay.jsx | |- FormInput.jsx |-containers | |- AppContainer.jsx | |- FormApp.jsx | |- Page2.jsx | |- Page2Container.jsx |-reducers |- fetchReducer.jsx |- formreducer.jsx |- index.jsx
index.jsx
import React from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import { createStore, applyMiddleware } from 'redux'; import { Provider } from 'react-redux'; import { reducers } from './reducers/index.jsx'; import thunkMiddleware from 'redux-thunk'; import AppContainer from './containers/AppContainer.jsx'; import Page2Container from './containers/Page2Container.jsx'; const initialState = { formReducer: { value : null }, fetchReducer: { items: [] } }; const store = createStore(reducers, initialState, applyMiddleware(thunkMiddleware)); // reducer, initial state // Rendering ReactDOM.render(変更したのは, redux-thunk をmiddlewareの設定にしたことこれで ActionCreatorでfunctionが返せます。, document.querySelector('.content') );
reducers/fetchReducer.jsx
export function fetchReducer(state = {}, action) { switch(action.type) { case 'REQUEST_DATA_SUCCESS': return Object.assign({}, state, { items: action.data, lastUpdated: action.receivedAt }); case 'REQUEST_DATA_FAIL': { return Object.assign({}, state, { items: action.data }); } default: return state; } }items というデータのArrayを返します。これはデータのリクエストが成功した時(その時はデータ) 、失敗した時に返します(その時は空[])
reducers/index.jsx
import { combineReducers } from 'redux'; import { formReducer } from './formreducer.jsx'; import { fetchReducer} from './fetchReducer.jsx'; export const reducers = combineReducers({ formReducer, fetchReducer });前回作成した、reducerと共に利用するので2つのReducerをcombineReducersで 繋げます。
actions/index.jsx
const SEND = 'SEND'; /* Action Creator */ // Return Object export function send(value) { // Action return { type: SEND, value, }; } /* Action2 */ const REQUEST_DATA = 'REQUEST_DATA'; const REQUEST_DATA_SUCCESS = 'REQUEST_DATA_SUCCESS'; const REQUEST_DATA_FAIL = 'REQUEST_DATA_FAIL'; function invalidateData() { return { type: REQUEST_DATA_FAIL, data: [] } } function requestData() { return { type: REQUEST_DATA } } function receiveData(json) { return { type: REQUEST_DATA_SUCCESS, data : json, receivedAt: Date.now() } } export function fetchData() { return function (dispatch) { dispatch(requestData()); return fetch('/data/data.json') .then(response => response.json()) .then(json => dispatch(receiveData(json.data))) .catch(e => dispatch(invalidateData())); } }前回作成したActionCreatorに3つのActionを追加しました。 データ取得できなかった場合のAction, リクエスト開始時のAction, データが取得できた場合のActionです。これらのアクションをまとめた fetchデータというfunctionをexportしています。
containers/Page2.jsx
import React from 'react'; import PropTypes from 'prop-types'; import {fetchData} from '../actions/index.jsx'; class Page2 extends React.Component { constructor(props) { super(props); } componentDidMount() { const { dispatch } = this.props; // console.log(dispatch); dispatch(fetchData()); } render() { const { items } = this.props; var list = []; for(var index in items){ list.push(
-
{list}
最後は Containerですね。 containers/Page2Container.jsx
// Connect to Redux function mapStateToProps(state) { return { items: state.fetchReducer.items } } const Page2Container = connect( mapStateToProps )(Page2) export { Page2Container as default };この部分でPropertyとComponentを繋ぎます。
テスト
動作確認webpack-dev-serverlocalhost:8080/member にアクセス。2つのアイテムのあるリストが見えるはずです。