LoginSignup
0
0

More than 3 years have passed since last update.

Reduxを使ってグローバルステートを扱う方法を倉庫に例えて説明します。

Last updated at Posted at 2020-04-03

Reduxを使えるようになりたいけど、なんだか難しそうで手が付けられない」

という人向けに、Reduxを使ってグローバルステートを扱う方法を倉庫に例えて説明します。

なお、Reactを使用することが前提になります。

Reduxを使用したサンプル

サンプル(CodeSandBox)
3cnya-sprx3.gif
各ページで単一のStateを扱っています。
上記のサンプルはTypeScriptと、ページ遷移にreact-router-domを使用しています。

Reduxの使い方

1. Redux(とか)をインストールする

npm i redux react-redux @types/react-redux

または、

yarn add redux react-redux @types/react-redux
  • redux:JavaScriptアプリ用の予測可能な状態コンテナー(公式ドキュメントより)。とりあえずこれは必須です。
  • react-reduxReduxのHooksが使えるようになります。
  • @types/react-reduxReact-Reduxの型定義ファイル。TypeScriptを使わない場合はインストール不要です。

2. グローバルステートをどのようにして扱うかをざっくり把握する

コードを書くことから一旦離れますが、全体像をざっくりとでも把握しておいた方が後の工程で理解が必要となる概念や用語が分かりやすくなるので、前述のサンプルにおいてプラスボタンを押した際の流れと、流れに関わる用語をまとめた下図をご確認ください。
redux.jpg
上図に出てくる用語の意味は私の解釈および流れの説明のために記載したものです。
なので、用語の意味が正しいかどうかはひとまず置いてください:joy_cat:
(「Redux 流れ」や「Redux 図」などで検索すると、より信頼できる図が確認できます)

上図で出てくる用語は下記の通りです。

  • Store(グローバルステートの倉庫)
  • Action(行動)
  • Type(Actionの内容)
  • Dispatch(送信)
  • Reducer(グローバルステートの処理係)

これらを踏まえて、次項からサンプルのコードのReduxに係る部分を説明します。

3. 画面を作る

特筆することは無いので割愛します。
好きに書いたらいいです。

1.JPG

4. storeを設ける

  • index.tsx

import { createStore } from "redux";
import { Provider } from "react-redux";

// Store(グローバルステートの倉庫)を設ける
const store = createStore(/* Reducer(グローバルステートの処理係。後で設定。 */); 

const rootElement = document.getElementById("root");
render(
  <Provider store={store}> // Storeと各コンポーネントを接続する
    <App />
  </Provider>,
  rootElement
);

上のコードに唐突に現れたProviderは流れ図には無かったものですが、コード内のコメントの通りStoreと各コンポーネントを接続するものと捉えてもらえればよいです。

これで下図のようなStoreができました。
2.JPG
ここまでの進捗を実際の倉庫で例えると、

  • 倉庫を建設した(createStore
  • 倉庫と倉庫の物資を必要とする場所との輸送ルートを設けた(Provider
  • 物資や人員はまだ何も無い

5. Reducerを設定する

Storeを作ったものの、現状Reducerを設定していないのでStoreは空っぽです。
ということでReducerを用意して、createStoreの引数に設定します。

  • /reducers/counterReducer.ts

const counter = (count = 0 /* グローバルステートcountの初期値 */, action: { type: string }) => {
  switch (action.type) {
    case "INCREMENT": // プラスボタンを押した場合
      return count + 1;
    case "DECREMENT": // マイナスボタンを押した場合
      return count - 1;
    default:
      return count;
  }
};
export default counter;

このコードにより、

  • Storeにグローバルステートcount(初期値0)を設定した
  • Stateに対する処理をActionのType別に設定した

ということになります。
Reducerは、画面からDispatch(送信)されたAction(行動)のType(内容)によって、Store(倉庫)にあるcountStateに処理を施します。
caseの文字列"INCREMENT""DECREMENT"については次項で説明します。

Reducerが用意できたのでStoreに設定します。

  • index.tsx

import { createStore } from "redux";
import { Provider } from "react-redux";
import counter from "./reducers/counterReducer"; // 用意したReducerをインポート

const store = createStore(counter); // Reducerを引数に設定

const rootElement = document.getElementById("root");
render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
);

これでStoreは完成です。
3.JPG
ここまでの進捗を実際の倉庫で例えると、

  • 倉庫に物資を格納した(count = 0
  • 業務内容を定めた人員を雇用した(Switch
  • その人員を倉庫に配属した(createStore(counter)

6. ActionのTypeを設定する

  • 画面が起こすActionはどんなものがあるか
  • それらActionをどのように定義するか

を設定します。

  • /actions/counterAction.ts
// プラスボタンを押すAction 
export const increment = () => {
  return {
    type: "INCREMENT" // このActionのTypeを"INCREMENT"と定義
  };
};

// マイナスボタンを押すAction
export const decrement = () => {
  return {
    type: "DECREMENT" // このActionのTypeを"DECREMENT"と定義
  };
};

この設定を実際の倉庫に係る業務で例えると、倉庫の物資を必要としている場所が、物資の供給を申請する際に使用する申請書を作成する業務にあたるかと思います。
このように捉えると上のコードは、

  • 申請内容別に申請書を作成した
  • そのうちの1種の申請書の名前を"INCREMENT"、もう1種を"DECREMENT"と命名した

ということになります。
画面さんはcountに+1した値が欲しい場合、"INCREMENT"申請書をStoreに勤務しているReducerさんにメールで送信するイメージです。

ここで1つ前に遡って、ReducerのSwitch文の内容をご確認ください。
Switch文がStoreに勤務しているReducerさんの業務内容です。

  • /reducers/counterReducer.ts

  switch (action.type) { // 「申請書の内容をチェックするよ」
    case "INCREMENT": // 「画面さんから送ってもらった申請書は"INCREMENT"だ」
      return count + 1; // 「じゃあcountに+1してcountを画面さんに渡さなきゃ」
    case "DECREMENT":
      return count - 1;
    default:
      return count;
  }

画面さんが"INCREMENT"申請書を送信した例に倣うと、上記コードのコメントの通り、Reducerさんは送信された申請書の内容を確認し、内容に沿った処理を行ないます。

以上でActionと、ActionのTypeを定義できました。
5.JPG

あとはこのActionを画面が持てば、グローバルステートを扱えます。
先の例えで言うならば画面さんが申請書を持つことで、画面さんと倉庫との物流システムが完成します(倉庫に申請書があってもしょうがないですよね)
ここまでの進捗を実際の倉庫で例えると、

  • 倉庫の物資を必要とする場所が、物資の供給を倉庫に申請する申請書を作成した(counterAction.ts

7. ActionをDispatchする

これまでの工程で仕組みを用意できたので、あとは画面からグローバルステートを呼び出したりsetStateするコードを記述することで、「Reduxを使ってグローバルステートを扱う」ことができるようになります。

下記はグローバルステートを扱いたいコンポーネントのコード例です。

  • App.tsx
import React from "react";

const Home = () => {
  return (
    <>
      <h1>Home</h1>
      <p>count: </p>
      <button onClick={() => {}}>+</button>
      <button onClick={() => {}}>-</button>
    </>
  );
};

このコードで下図のような画面ができると思います(できなかったらごめんなさい:joy:
5.JPG

count:のコロンの後はcountの値を表示したいですが、何も表示されていません。
なのでStoreからグローバルステートであるcountを取得し、ここにcountの値を表示しましょう。

  • App.tsx
import React from "react";
import { useSelector } from "react-redux"; // useSelectorをインポート

const Home = () => {
  const count = useSelector((count: number) => count); // countをStoreから取得

  return (
    <>
      <h1>Home</h1>
      <p>count: {count}</p> {/* countを表示 */}
      <button onClick={() => {}}>+</button>
      <button onClick={() => {}}>-</button>
    </>
  );
};

突然登場したuseSelectorは実際に機能している状態を見れば分かる通り、StoreからStateを取得するHooksです。
これでcount:のコロンの後に、グローバルステートであるcountの値が表示されます。
7.jpg
最後に、ボタンを押すことでグローバルステートの値を増減させ、その値を画面に表示しましょう。
現状はプラスボタンを押してもマイナスボタンを押しても、count: 0の値は変わりません。
下記コードのコメントが入っているコードを追加することで、グローバルステートに対してsetStateできます。

  • App.tsx
import React from "react";
import { useSelector, useDispatch } from "react-redux"; // useDispatchをインポート
import { increment, decrement } from "./actions/counterAction"; // Actionをインポート

const Home = () => {
  const count = useSelector((count: number) => count);
  const dispatch = useDispatch(); // dispatch関数を作成

  return (
    <>
      <h1>Home</h1>
      <p>count: {count}</p>
      {/* Action"increment"をDispatch */}
      <button onClick={() => dispatch(increment())}>+</button>
      {/* Action"decrement"をDispatch */}
      <button onClick={() => dispatch(decrement())}>-</button>
    </>
  );
};

これでプラスボタンを押すとcountの値が+1され、マイナスボタンを押すとcountの値が-1されるようになりました。

ここで改めてプラスボタンを押した際の流れ図をご確認ください。
前の流れ図に比べて、より実装に沿った図にしています。
8.jpg
以上でReduxを使ったグローバルステートを扱う構成が構築できました。

実際の倉庫で例えると、

  • 倉庫から物資を受け取った(useSelector)
  • 倉庫に物資の供給を申請できるようになった(useDispatch)

という体制ができたことになるかと思います。

おわり

今までの説明はReduxのことが全く分からない人向けの説明なので、今までの説明が理解できた場合は、Redux公式ドキュメントを読んでReduxに対する認識をアップデートしてください。
「前に公式ドキュメントを読んだけど全然意味が分からなかった」という人は、前に比べれば何を言っているか分かる状態になっていることと思います。
というかそうであることを願っています。

Reduxがどうしても理解できなかった場合

もっとシンプルにグローバルステートを扱えるReactNというライブラリがあります。
グローバルなステートをめっちゃシンプルに扱えるReactN - Qiita

0
0
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
0
0