Horic Design

a-blog cmsやJavaScriptなどの情報を発信しています。

ReactとReduxを連携してみた

ReactはUIライブラリ、Reduxはステート管理ライブラリということは前から知っていた。そしてそれら二つは決して難しいライブラリではない。しかしReactとReduxをどう連携するのか、その部分についてはいくら記事を読んでもどうも釈然としなかった。
そこでこの前一日使って、本腰をいれて勉強してみた。すると思ってたよりもずっと簡単だったのでそれについてまとめようと思う。

用語の解説

Reduxを理解するにあたって覚えなければならない用語が下の3つ。

  • State
  • Reducers
  • ActionCreators

State

アプリケーションの状態。StateをReducerを通じてのみ新しいStateに変更される。このStateはreact-reduxでconnectされたReactコンポーネントのpropsより参照することが可能。

Reducers

Stateの値を更新するための関数がまとまったもの。stateのプロパティをあらたな値に書き換えるときはお作法として、Object.assign();を使う。イメージとしてはjQueryのextend。
ReducerはActionCreatorsから呼び出され実行される。

const initialState = {
  count: 0,
  name: 'ほげ'
};

export default (state = initialState, action) => {
  switch (action.type) {
    case types.INCREMENT:
      return Object.assign({}, state, { count: state.count + 1 });
    case types.DECREMENT:
      return Object.assign({}, state, { count: state.count - 1 });
    case types.RENAME:
      return Object.assign({}, state, { name: action.name });
    default:
      return state;
  }
};

ActionCreator

下のように、どのActionを実行するか、そしてそのActionにどんな値を渡すかを定義したObject(Actions)を返す関数。このActionCreatorよりReducersが実行される。

export const increment = () => ({ type: types.INCREMENT });
export const decrement = () => ({ type: types.DECREMENT });
export const rename = (name) => ({ type: types.RENAME, name: name });

ReactとReduxの連携

そして肝心なのがReactとReduxの連携。連携するためにreact-reduxというモジュールを使い、stateとdispatchをpropsにbindしたcontainer(別名:connected component)と呼ばれるReactコンポーネントを作る。下記のコードではmapDispatchToPropsmapDispatchToPropsをconnectすることで、stateもdispatchも両方、this.props直下でよびだせるようしてある。例えば、先ほど作成した、increment()のActionCreatorはthis.props.increment()で呼び出すことができる。またこのとき bindActionCreators という関数をつかうことで、登録されたActionを一度に紐付けることができる。

import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import React from 'react';
import * as Actions from '../actions';
import Hoge from '../components/hoge';

class App extends React.Component {
  ...
}

function mapStateToProps(state) {
  return state;
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(Actions, dispatch)
}

export default connect(mapStateToProps, mapDispatchToProps)(App);

そしてmain.jsで以下のようにContainerとStoreを結びつける。StoreはStateを管理するためのもの。

import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';

import React from 'react';
import App from './containers/App';
import reducer from './reducers';


const store = createStore(reducer);

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('app'),
);

各コンポーネントへのAction及びStateの紐付けについて

Reactでは親コンポーネントから子コンポーネントへの値の受け渡しはどうしてもPropsリレーになる。つまり親のPropsを再び子propsに渡してあげる必要がある。ただ、Reduxを使う時に定義したdispatchなどが素直に使えないと少しストレスを感じるので自分はJSX Spread Attributesを使う。{...props}のような記法だ。これを使うことで、親のPropsをごそっと子供に渡すことが可能となる。

class App extends React.Component {
  render() {
    const props = this.props;
    return (<div>
      <Hoge {...props}/>
    </div>);
  }
}

子コンポーネントからstateの参照とactionの実行

import React, { Component, PropTypes } from 'react'

export default class Hoge extends React.Component {
  render() {
    return (<div>
      <div>{this.props.name}: {this.props.count}</div>
      <button onClick={() => this.props.increment()}>増加</button>
      <button onClick={() => this.props.decrement()}>減少</button>
      <input type="text" onChange={(e) => this.props.rename(e.target.value)} value={this.props.name} />
    </div>);
  }
}

以下にサンプルのソースを置いておく。



堀 悟大

有限会社アップルップル マークアップエンジニア。2014年高知大学理学部卒業。学生時代にHTML5のCanvas要素を使ってゲームを作っていたことでWeb全般に興味をもつ。アップルップル入社後はa-blog cmsを便利に使うための機能の実装や、HTML5の技術を使ったデジタルサイネージの実装を行う。趣味は英語。読むことも話すことも好き。

エントリーリスト

カテゴリーリスト

タグクラウド