LoginSignup
6
5

More than 5 years have passed since last update.

Reduxについて勉強した #1

Last updated at Posted at 2016-12-05

本記事はReduxのチュートリアルであり、Reactについての詳しい説明はここでは扱わない。

基本的にはこちらのビデオチュートリアルをベースに書いている。英語に問題がなく、ビデオが嫌いでなければこちらを参照した方が正直わかりやすいかと思う。

下準備

gitとNodeはインストール済みであるとする。まずはターミナルでスターターキットをダウンロードする。このスターターキットにはReact、Webpack、babelが含まれている(Reduxは含まれていない)。

$ git clone https://github.com/alicoding/react-webpack-babel react-counter
$ cd react-counter
$ npm install
$ npm run start

http://localhost:8888/にアクセスして正しく表示されれば成功。ターミナルでCtrl + Cを押して一度キャンセルする。

Reduxとは

Reduxは、アプリの状態を管理するためのシンプルなライブラリである。ここで、いくつか用語が出てくる。まずReduxでは、アプリの状態( state )と、状態を変更するためのアクション( action )をそれぞれ定義する。

これらは、例えばこれから作るカウントアプリでは以下のようになる。

state: アプリは現在カウンターの数値がいくつなのか、という状態。
action: 「+1をする」「-1をする」が、それぞれアクション

つまりReduxでは、stateに対してactionを実行して新しいstateにしていく。これを行う関数を reducer と呼ぶ。また、アプリは状態(state)を保持する変数を1つだけ持ち、その変数を store と呼ぶ。

すなわちアプリの状態( state )は全て store によって保持され、 reducer の関数を通して action が実行されることによってのみ変更される。そして状態が変更された際に再描画をすることで常に最新の状態がユーザーから見えるようになる。図にすると以下のようになる。

redux-image.png

state, action, store, reducerは以後よく出てくるので、ここで理解しておきたい。

なぜReduxを使うのか?

Reactで考えるとわかりやすく、次のようなメリットがある。

  • Reactのコンポーネント階層が深くなった時のバケツリレーを回避
  • デバッグやテストの自動化が簡単になる

ちなみにReduxはReactに限らず他のJavascriptライブラリでも使うことができる。

Reduxは必ず使わなければいけないものではない

Reduxのインストール

npmを使ってインストールする。

$ npm install --save redux

カウントアプリの作成

まずはReactを使わず、Reduxのみでカウントアプリを作成する。src/index.jsxを次のように書き換える。

src/index.jsx
import {createStore} from 'redux';

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

const store = createStore(counter);

const render = () => {
  document.body.innerText = store.getState();
}

store.subscribe(render);
render();

document.addEventListener('click', () => {
  store.dispatch({ type: 'INCREMENT' });
});

npm run startを実行してhttp://localhost:8888/にアクセスする。ページ内のどこかをクリックした時に数値が増えていけば正しく動作している。

このコードについて正しく理解しよう。まず最初の行はReduxのライブラリからcreateStoreという関数を使えるよう、インポートしている。

次にcounterという関数を作っている。この関数が、前述したreducerになっている。引数としてstateactionを受け取り、それらに応じて新しいstateを返り値として返却する。ここでは、action.typeが'INCREMENT'のときはstateを+1し、'DECREMENT'のときはstateを-1している。

このreducerを元に、Reduxによってstoreという変数を作っている。名前の通りstoreとなる。

そして、stateが更新された際の処理としてrender関数を定義し、subscribeしている。render関数では単にページに最新のstate(現在のカウント)を表示している。

最後に、ページ内がクリックされた際、storeに対してactionを発行(dispatch)している。コードの通り、actionはtype要素を持つオブジェクトである。

createStoreの実装はどのようになっているのか?

ReduxのcreateStoreの中では何が行われているのだろうか。面白いことに、実装は以下のように非常にシンプルである(実際には例外処理など、もう少しだけ長いコードになっている)。

const createStore = (reducer) => {
  let state;
  let listeners = [];

  const getState = () => state;

  const dispatch = (action) => {
    state = reducer(state, action);
    listeners.forEach(listener => listener());
  };

  const subscribe = (listener) => {
    listeners.push(listener);
    return () => {
      listeners = listeners.filter(l => l !== listener)
    }
  };

  dispatch({});

  return { getState, dispatch, subscribe };
}

コードの通り、storeはstateと、listeners(状態の変更を監視しているイベントリスナー達)を持つ。また、現在のstateを取得するgetState、そしてactionを発行するdispatch、状態の変更を監視するためのsubscribeという3つの関数を持つ。statelistenersは外部から直接変更することができないようになっている。

Reactを使って描画する

カウントアプリをReactで描画するように変更する。コードはほとんど変わらない。まずsrc/app.jsxを以下のように変更し、数字を表示する見出しと、「+1」と「-1」の2つのボタンを置く。

src/app.jsx
import 'bootstrap/dist/css/bootstrap.min.css';
import React from 'react';

export default class App extends React.Component {
  render() {
    return (
      <div>
        <h1>{this.props.value}</h1>
        <button className='btn btn-primary' onClick={this.props.onIncrement}>+</button>
        <button className='btn btn-danger' onClick={this.props.onDecrement}>-</button>
      </div>
    )
  }
}

src/index.jsxの方ではReactDOMによってこれを描画する。全体のコードは以下のようになる。

src/index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux';
import App from './app.jsx';

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

const store = createStore(counter);

const render = () => {
  ReactDOM.render(
    <App
      value={store.getState()}
      onIncrement={ () =>
        store.dispatch({
          type: 'INCREMENT'
        })
      }
      onDecrement={ () =>
        store.dispatch({
          type: 'DECREMENT'
        })
      }
    />,
    document.getElementById('app')
  )
}

store.subscribe(render);
render();

プラスボタンとマイナスボタンのあるカウントアプリが完成した!

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5