1
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

React Redux の使い方

Last updated at Posted at 2020-12-30

お仕事で React Redux を使用することになったので、学習した事柄をここにまとめます。

学習の指針

React Redux について理解するためには、まず Redux がどのようなものか調べ、それから React Redux に進むのが吉です。

Redux はそれなりにコンテクストヘビーなパッケージなので、React Redux のドキュメントは Redux を理解している前提で書かれているものがほとんどです。なので、一足飛びに React Redux へ進もうとしても、結局 Redux のドキュメントから読み直す羽目になります。

従って、まずは Redux の公式ドキュメントを読むのがよいです。
以下の順に読むのが React Redux に到達する最短経路だと思います。

とはいえ、一時的に React Redux の使い方だけを知りたい方もいると思うので、この記事では React Redux の使い方に絞って内容を記述をします。

具体的には、何を解決するために Redux が必要になるのかを簡単に述べ、その後で React Redux を動作させるための最小限のサンプルを示します。

React Redux は何を解決してくれるか

React をはじめ、画面をコンポーネントに分割するライブラリを使っていると、しばしば複数のコンポーネントで状態を共有する必要に迫られます。その際に「その状態はどのコンポーネントで管理するべきか?」という微妙な問題が生じます。

状態を共有すべきコンポーネントがごく近い場所にあるうちは、React が提唱する Lifting State Up といった手法を使うことで、親コンポーネントに状態の管理を委譲できます。しかし、アプリケーションの規模が大きくなってくると、それだけでは対応できないケースも出てきます。

遠く離れたコンポーネント間で状態を共有するナイーブな方法は、深い階層の親子関係を辿って状態のバケツリレーを行うことです。しかし、この方法では中間層のコンポーネントが無駄な状態で汚染されるため、可能であれば別の方法を探りたいところです。

Redux は、上記の問題を「コンポーネントツリーの中で状態管理をするのは無理なので、コンポーネントツリーの外で管理しよう」という思想によって解決するためのライブラリです。

Redux では、コンポーネントツリーのどこからでもアクセス可能な、グローバルな空間の中で状態を一元管理します。そして状態が更新された場合、それを検知する必要があるコンポーネントに対して、更新内容をフィードバックします。

一見、グローバル変数を彷彿とさせるアイデアですが、Redux では、状態を管理する空間に適切な構造を持たせることにより、これを容易に管理可能なものとしています。

結論ですが、React Redux を導入する必要があるかどうかは、ひとえに、

  • 画面の複数のコンポーネントから参照される複雑な状態があるかどうか

によって判断するのがよいかと思います。

Redux の構成

Redux は Action・Reducer・Store・Dispatch・Selector という5つの要素から構成されます。
それぞれの役割は以下の通りです。

  • Action: 状態更新のトリガーとなるイベントを定義する
  • Reducer: 各 Action が発行された際の状態の更新方法を定義する
  • Store: Reducer で定義した更新方法に則って状態を一元管理する
  • Dispatch: Action を発行する
  • Selector: Store から状態を取得する

全体の流れは、公式ドキュメントにある こちらの概念図 がわかりやすいです。
シンプルな構成なので、あとは実際のコードを読めば大体わかります。

各概念の詳細については、他に説明している記事がたくさんあると思うので、この記事では割愛します。

サンプルコード

React Redux を動作させる最小限のサンプルコードを示します。
作成するのは React Redux を使用した簡単なカウンタです。

サンプルコードは Github で公開してあるので、とにかく動かしたい方はこちらをどうぞ。

準備

サンプルコードは create-react-app で作成されるテンプレートを前提とします。

$ create-react-app react-redux-sample
$ cd react-redux-sample
$ npm install redux react-redux

ついでに必要なファイルも作っておきます。

$ mkdir redux
$ touch redux/action.js
$ touch redux/store.js

Action

Action は type プロパティを持つ JavaScript のプレーンオブジェクトです。
type はイベントの種類を表すラベルと考えておけば大丈夫です。

以下に Action の定義を示します。
ここではカウンタのインクリメント・デクリメントの Action を定義しています。

redux/action.js
export const ActionType = {
  increment: 'increment',
  decrement: 'decrement',
};

export const increment = {
  type: ActionType.increment,
};

export const decrement = {
  type: ActionType.decrement,
};

ここでは type のみを持つオブジェクトを作成していますが、それ以外のプロパティを持たせることも可能です。追加したプロパティは次に示す Reducer の中で参照可能です。

Reducer / Store

各 Action に対する状態遷移を定義した Reducer と、それを元に状態を管理する Store を作成します。

このサンプルでは、状態はカウンタの値のみを持つオブジェクトです。
Reducer の中でカウンタの値をインクリメント・デクリメントする状態遷移を表現しています。

redux/store.js
import { createStore } from 'redux';
import { ActionType } from './action';

const initialState = {
  count: 0,
};

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case ActionType.increment:
      return { ...state, count: state.count + 1 };
    case ActionType.decrement:
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
};

const store = createStore(reducer);

export default store;

React Redux ではコンポーネントからアクセスする Store を指定するのに Provider というタグを使います。Provider タグの store プロパティに作成した Store を渡すことで、全ての子コンポーネントから、引き渡した Store にアクセスできるようになります。

ここでは index.js に App タグを囲う形で Provider タグを追加します。

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import { Provider } from 'react-redux';
import store from './redux/store';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

Dispatch / Selector

App.js を書き換えて、Store で管理されているカウンタの値を表示します。また、カウンタの値をインクリメント・デクリメントするためのボタンを追加します。

Store の値を取得するために Selector を使用します。React Redux が提供している useSelector フックを使用することで、Store の値が更新された際に、自動的にコンポーネントを更新してくれるようになります。

ボタンの onClick には Dispatch を使用して Action を発行するコールバックを登録します。これにより Reducer 経由で Store の状態が更新されます。Dispatch の取得には useDispatch フックを使用します。

App.js
import './App.css';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './redux/action';

const App = () => {
  const count = useSelector(state => state.count);
  const dispatch = useDispatch();
  return (
    <div className="App">
      <p>{count}</p>
      <button onClick={e => dispatch(increment)}>Increment</button>
      <button onClick={e => dispatch(decrement)}>Decrement</button>
    </div>
  );
}

export default App;

以上で React Redux を動作させる最小限のサンプルコードができました。
あとは一連の流れを確認して、煮るなり焼くなりすればよいと思います。

おまけ

Redux は React の設計思想である Flux を参考に書かれているそうなので、Flux について知っておくと理解が深まるかもしれません。時間がある時に以下のサイトを読んでおくと良さそうです。

1
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
1
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?