あらすじ
前回は 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 について触れます。