読者です 読者をやめる 読者になる 読者になる

【React実験】 Reduxを試してみる

React JavaScript

読み方はリダックス。リデュークスではない。

redux.js.org

state の管理がしやすくなるエコシステム。名前をよく聞くので試してみた。

f:id:kosumoro:20160830001133j:plain

https://cosmology233.github.io/sandbox/hatena/2016/08/30/

  1. ディレクトリ構成
  2. 各ファイルの中身
  3. まとめ
  4. 参考

ディレクトリ構成

scripts/ 以下のファイルは browserify でバンドルして main.js にまとめる。

source/
  + scripts/
  |  + components/
  |  |  + app/
  |  |     + component.jsx
  |  |     + actions.js
  |  |     + container.js
  |  |     + reducer.js
  |  |   
  |  + reducer.js
  |  + renderer.js
  |  + main.js
  |
  + index.html

各ファイルの中身

index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Redux Tutorial</title>
</head>
<body>
  <div id="app"></div>

  <script src="./scripts/main.js"></script>
</body>
</html>

Reactコンポーネントを描画する div と、 main.js を読み込むための script のみ。

scripts/main.js

import renderer from "./renderer";

renderer を読み込んでいるだけ。

scripts/renderer.jsx

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducer from "./reducer";
import App from "./components/app/container";

let store = createStore(reducer);

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

state を一元管理するための store を作成し、 store を使用して Redux の恩恵にあやかるためのコンポーネント Provider で他コンポーネントをラップしている。

scripts/reducer.js

import { combineReducers } from "redux";
import appReducer from "./components/app/reducer";

export default combineReducers({
  "app": appReducer
});

コンポーネントごとに作成している Reducer をひとつにまとめる。

scripts/components/app/component.jsx

コンポーネントごとにディレクトリを分けてみた。公式のチュートリアルでは別の分け方をしている。

import React from "react";

export default class App extends React.Component {
  render() {
    return (
      <div>
        <p>{this.props.count}</p>

        <p>
          <button
              onClick={this.props.handleClick}>
            Click!
          </button>
        </p>
      </div>
    );
  }
}

普通の React コンポーネント

scripts/components/app/actions.js

export default {
  "increment": () => {
    return {
      "type": "INCREMENT"
    };
  }
};

Redux では Action は type プロパティを含むオブジェクトを指す。上のソースで言うと { "type": "INCREMENT" } が Action にあたる。

Action を返す関数を Action Creator という。

actions.js では Action Creator のセットを定義している。

scripts/components/app/container.js

import React from "react";
import { connect } from "react-redux";
import Component from "./component";
import Actions from "./actions";

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

function mapStateToProps(state) {
  return state.app;
}

function mapDispatchToProps(dispatch) {
  return {
    "handleClick": () => {
      dispatch(Actions.increment());
    }
  };
}

ReactコンポーネントをReduxのエコシステムにつなげている。

mapStateToProps は Redux で管理している state が更新された際に、 Reactコンポーネントで使用する props を更新してくれる。

mapDispatchToProps は、おそらく、 Redux の store に対して state の変更依頼を送信する関数を定義して、その定義した関数を React の props に渡してくれるもの、だと思う。

scripts/components/app/reducer.js

let initialState = {
  "count": 0
};

export default function reducer(state = initialState, action) {
  switch(action.type) {
    case "INCREMENT":
      return Object.assign({}, state, {
        "count": state.count + 1
      });

    default:
      return state;
  }
}

Action を受け取って、 state を更新してくれるのが Reducer。

まとめ

まだふんわりとしか分かっていないが、趣味で行うようなごく小さいアプリケーションの開発にはそこまで深い理解は必要ないように思える。

いろいろ試しながら使っていればそのうち分かってくるんじゃないかと思う。

参考

ありがとうございました。