LoginSignup
31
30

More than 5 years have passed since last update.

Reduxを単体で使って動作を理解する

Posted at

Reduxが何をやっているのかがわからない。
React + Reduxではない。
Redux単体だ。
Reduxについて調べるとやれReactとの連携とか、
Fluxについて高尚な演説が永遠と続く。
で、結局Reduxって何やってるの?と無限ループしてしまう。
そこでRedux以外は本当に何も使わずに簡単なアプリを作ってみる。

どういったアプリを作るか

やみくもに作ってもしょうがないので仕様を決める。

  • フォームに画像のURLを入力してSetボタンを押すとURL情報を内部に保存する
  • Getボタンを押すと内部に保存されたURLの画像を表示する

Reduxの特性を活かすべく、
内部に状態を持ってビューの出し分けをすることを考える。

HTML

まず以下のようなHTMLを用意する。

index.htm
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <title>title</title>
</head>
<body>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.6.0/redux.min.js"></script>
<script src="main.js"></script>
</html>

<body>タグは漢らしく空で、
CDNからreduxのjsをロードして処理の本体はmain.jsに書くという構造だ。
Reactとかnpm installとかはカケラも出てきてない。
なお以降の操作はChromeで行ってもらいたい。
ES6のコードがある程度そのまま動くので。

初期化

main.js
// 初期化
(() => {
    document.body.innerHTML = `
    <button onclick="actionSetURL(document.getElementById('url').value)">Set</button>
    <button onclick="actionGetImg()">Get</button>
    <button onclick="actionClear()">Clear</button>
    <input id="url" type="text" style="width: 600px;" />
    <div id="render"></div>
  `;
})();

bodyに骨格となるHTMLを直接流し込んでいるだけです。
ボタンや入力フォームの作成を行っています。
actionSetURL()ではフォームに入力された値を引数として渡していて、
actionGetImg()、actionClear()は引数なしということがわかるかと思います。
続いてこの3つの関数を書いていきます。

Action

Actionとは後述するStoreを変更するための機構である。
typeは必須で他の引数は任意である。
typeは全て大文字のスネークケースで書くのが習わしらしい。
Storeを更新したいときはActionを発行するのが基本パターンだ。

main.js
// Action
const actionSetURL = url => url && store.dispatch({ type: 'SET_URL', url });
const actionGetImg = () => store.dispatch({ type: 'GET_IMG' });
const actionClear = () => store.dispatch({ type: 'CLEAR' });

Reducer

ReducerとはActionと現在のStateを受け取って、
新しいStateを返す関数である。

main.js
// Reducer
const reducerURL = (state = '', action) => {
  switch (action.type) {
    case 'SET_URL':
      return action.url;
    case 'CLEAR':
      return '';
    default:
      return state;
  }
}
const reducerImg = (state = false, action) => {
  switch (action.type) {
    case 'GET_IMG':
      return true;
    case 'CLEAR':
      return false;
    default:
      return state;
  }
}

この例だとreducerURLはstringを返していて
action.typeがSET_URLのときはフォームに入力した値を返し、
action.typeがCLEARのときは空文字を返す。
reducerImgはbool変数を返している。

Reducerをまとめる

main.js
const combineReducers = reducers => (state = {}, action) => Object.keys(reducers).reduce((nextState, key) => {
  nextState[key] = reducers[key](state[key],action);
  return nextState;
}, {});
const rootReducer = combineReducers({ reducerURL, reducerImg });

先の例ぐらいだと1つのReducerに書いてもいいかもしれないが、
規模が大きくなると分割する必要が出てくる。
複数のReducerをまとめるcombineReducersを書いて
1つに集約したものをrootReducerとする。

Store、Provider、Render

StoreとはReducerで変更されたstateを保存する場所である。
Redux.createStore()にReducerを入れて作成する。
providerはstateを受け取ってHTMLを構築する関数だ。
stateReducer経由でしか更新されない読み取り専用オブジェクトだ。
読み取りにはstoreに対してgetState()を呼べばいい。
renderはproviderで構築されたHTMLを描写する関数だ。

main.js
// Store, Provider, Render
const store = Redux.createStore(rootReducer); // storeの作成
const provider = state => {
  const stateView = `
    <div>
      <div>state.reducerURL: ${state.reducerURL}</div>
      <div>state.reducerImg: ${state.reducerImg}</div>
    </div>
  `;
  const imgView = state.reducerImg ? `<img src='${state.reducerURL}' />` : '';
  return `
    <div>
      ${stateView}
      ${imgView}
    </div>
  `;
}
const render = () => { document.getElementById('render').innerHTML = provider(store.getState()); }
store.subscribe(render);
render();

まとめ

以上でアプリケーションは動作する。
actionからstateが変更されるまでの流れがよくわかった気がする。
providerあたりでやっているHTMLの構築をスッキリ書けるのがReactということなのだな。

31
30
2

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
31
30