あらすじ
前回は React の基本的な作法に触れました。 今回は Redux を使ってみます。
目次
- 開発環境を準備
- React の基本的な Life Cycle に触れる
- redux に触れる <= 今日やること
- redux-saga に触れる
- react router に触れる
- npm で公開されている components を導入して echo system を体感する
- redux-form に触れる
- react-select に触れる
今日やること
準備
npm install --save redux redux-actions redux-logger react-redux
mkdir src/containers src/store src/reducers touch src/containers/Posts.js src/actions.js src/store/configureStore.js src/reducers/index.js
各種ファイル作成
create src/actions.js
import { createAction } from "redux-actions" export const SET_POSTS = 'SET_POSTS' export const setPosts = createAction(SET_POSTS)
create src/reducers/index.js
import { combineReducers } from 'redux' import { SET_POSTS } from '../actions' const initialState = { posts: [] } function app(state = initialState, action) { switch (action.type) { case SET_POSTS: return Object.assign({}, state, {posts: action.payload}) default: return state } } const rootReducer = combineReducers({ app, }) export default rootReducer
create src/store/configureStore.js
import { createStore, applyMiddleware } from 'redux' import createLogger from 'redux-logger' import rootReducers from '../reducers' const logger = createLogger() const middlewares = [logger] const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore) export default function configureStore(initialState) { const store = createStoreWithMiddleware(rootReducers, initialState) if (module.hot) { module.hot.accept('../', () => { const nextRootReducer = require('../reducers').default store.replaceReducer(nextRootReducer) }) } return store }
create src/containers/Posts.js
import React, {Component} from 'react' import { connect } from 'react-redux' import { setPosts } from '../actions' import { PostItem } from '../components/Posts' import { posts } from '../fixtures' class Posts extends Component { handleButtonClick() { this.props.dispatch(setPosts(posts)) } render() { const {posts} = this.props.app return ( <div className="container"> <h2>Posts</h2> {posts.map((post) => ( <PostItem key={post.id} post={post} /> ))} <button className="btn btn-default" onClick={this.handleButtonClick.bind(this)}>Push</button> </div> ) } } const select = state => (state) export default connect(select)(Posts)
edit src/Root.js
import 'babel-polyfill' import 'bootstrap' import 'bootstrap/dist/css/bootstrap.css' import React from 'react' import { Provider } from 'react-redux' import configureStore from './store/configureStore' import Posts from './containers/Posts' const store = configureStore() export default (props) => ( <Provider store={store}> <Posts /> </Provider> )
Implement it
% node devServer.js
edit src/components/Posts.js
to remove wasted codes
import React, {Component} from 'react' // stateless component export const PostItem = (props) => ( <div> <h3>{props.post.title}</h3> <p>{props.post.body}</p> </div> )
containers と conmponents について
src/containers
と src/components
の違いについて明確な答えを私はよく知らないのですが、とりあえず connect
を使っているコンポーネントは containers に配置する。でいいと思います。
で、connect
していないものは containers に入れてはいけないのか?というとそうではないので、とりあずよく分からない場合は全部 containers に入れておいていいと思います。
私は共通化できる stateless な コンポーネントを見つけたときだけ src/components
に移動させるようにしています。
それから最初はconnect
が何やっているかわかりづらいと思います。これは親、子、孫 のコンポーネントがあった場合、親から孫までデータ渡して孫から親までイベントを伝播させるのではなく、直接孫にデータを渡して、イベントを引き取るために使っている。というイメージで覚えておいてとりあえず次に進んでしまってください。
まとめ
今回は redux について触れました 次回は redux-thunk をすっとばして redux-saga について触れます。