React + Reduxでカウンターアプリの作成 (react-redux使わない編)

目的

Reactでアプリを作る際はReduxを併用するのが現在のメインストリームぽいので簡単なアプリを作りながらReudxに入門することに。

Reduxのgithubリポジトリのexampleにあるカウンターアプリを作ります。(よりシンプルにするために”Increment if odd”ボタンと“Increment async”ボタンの実装はしていません) また、ReactとReduxを併用する際に使うのが当たり前になっているぽい?react-reduxは今回は使いません。
コードはGithubに置いてあります。

Reduxのデータフロー

今回作ったカウンターアプリはこちらで、
以下のようにReduxのデータフローをたどります。

  • [+]ボタンまたは[-]ボタンをクリック
  • Actionがdispatchされる。このときdispatchされるActionは
    [+]ボタンの場合 {type: INCREMENT}
    [-]ボタンの場合 {type: DECREMENT}
  • ReducerがdispatchされたActionを受け取り、typeによってStoreのstateを更新する。
    typeがINCREMENTの場合はstate + 1
    typeがDECREMENTの場合はstate – 1
  • Storeのstateが更新されるとView(数字)が更新される

src/index.js

ここからコードを見ていきます。

import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux';
import Counter from './components/Counter';
import counter from './reducers';

const store = createStore(counter);
const rootEl = document.getElementById('root');

const render = () => ReactDOM.render(
    <Counter value={store.getState()} onIncrement={() => store.dispatch({ type: 'INCREMENT'})}
        onDecrement={() => store.dispatch({ type: 'DECREMENT'})}
    />,
    rootEl
);

render();
store.subscribe(render);

ここでやっていることは

  • Reducerを引数にしてcreateStoreでStoreを作成
  • CounterコンポーネントにpropsとしてStoreのstate、Actionをdispacthする関数を渡す
  • store.subscribe(render)で、Storeを監視してStoreのstateが変更されたらrenderを実行して再描画

です。

src/components/Counter.js


import React, { Component, PropTypes } from 'react';
                               
class Counter extends Component {
    static propTypes = {
        value: PropTypes.number.isRequired,
        onIncrement: PropTypes.func.isRequired,
        onDecrement: PropTypes.func.isRequired
    }
 
    render() {
        const { value, onIncrement, onDecrement } = this.props;
        return (
                Clicked: {value} times
                {' '}
                <button onClick={onIncrement}>
                  +
                </button>
                {' '}
                <button onClick={onDecrement}>
                  -
                </button>
        );        
    }           
}               
export default Counter; 

ここでは

  • propsとして渡ってきたvalueの表示
  • 同じくpropsとして渡ってきたonIncrement, onDecrement関数を、[+]ボタンと[-]ボタンのクリック時に実行
  • static propTypesではpropsのバリデーションをしています。valueは数字で必須、onIncrement, onDecrementは関数で必須、といった感じです。

src/reducers/index.js

最後にReducerになります。

export default (state = 0, action) => {
    switch (action.type) {
        case 'INCREMENT':
            return state + 1;
        case 'DECREMENT':      
            return state - 1;
        default:
            return state;      
    }           
} 

コードを見ての通りでdispatchされてきたActionのtypeによって、Storeのstateを変更します。これによってコンポーネントが再描画されることになります。

まとめ

  • Actionがdispatch
  • ReducerでStoreのstateを更新
  • コンポーネントの再描画

という流れが分かりました。次回はこのカウンターアプリをreact-reduxを使って実装してみます。