LoginSignup
1
1

More than 1 year has passed since last update.

React Reduxの全体像とデータフロー

Posted at

はじめに

ずっとReduxから逃げて他のstate管理ライブラリを使っていたんですが、ついに捕まったので調査しました。

Reduxの全体像

image.png

要素 役割
Component Storeを利用するコンポーネント。ComponentはUIにのみ責任を持ち、ロジックはContainerに分離する。
Action Storeに対して、stateの参照や変更を依頼するための依頼書。ComponentがActionCreatorを通して作成する。
ActionCreator Componentから必要な情報を受け取り、Actionを作成する
Container ComponentとStoreを接続し、Componentに対してstateとdispatch()を提供する。stateに対して何がしかの処理を加えたい場合、Container内にロジックを記述する。
dispatch() actionをReducerに届け、処理を促す。
Store データストア。stateを集中管理する。Reducer以外からのstate変更を受け付けない。
Reducer Actionを受け取り、Store内のstateに変更を及ぼす。stateの種類によってReducerを分けるのが一般的。
CombineReducer 複数あるReducerをひとまとめにし、Storeと接続する。

ユーザアクションに応じて、Store内のstateを更新する際のデータフロー

下記流れでStore内のstateを更新します。

  1. Componentがイベントを検知し、Actionを作成する。
  2. 作成したActionを、Containerから提供されたdispatch()を用いて、Reducerに処理を依頼する。
  3. Reducerは、現在のstateとActionをもとに新たなstateを計算し、そのstateをStoreに反映させる。

Componentがイベントを検知し、Actionを作成する。

image.png

Componentはイベント(ユーザのクリックとか)を検知して、ActionCreatorを通じてActionを作成します。

この時、ComponentはActionCreatorに対して、下記の情報を提供します。

Type アクションの種類を識別するための文字列
Payload アクションに必要なデータ(引数)

ActionCreatorは上記をもとに、Actionを作成します。

Actionの例↓

{
    type: "todos/todoAdded",
    payload: todoText,
}

作成したActionを、Containerから提供されたdispatch()を用いて、Reducerに処理を依頼する。

image.png

ComponentはActionをDispatchして、Reducerに処理を依頼する。

index.js
dispatch({ type: 'todos/todoAdded', payload: 'todoText' })

Reducerの中身はこんな感じ↓

todosReducer.js
// Use the initialState as a default value
export default function appReducer(state = initialState, action) {
  // The reducer normally looks at the action type field to decide what happens
  switch (action.type) {
    // Do something here based on the different types of actions
    case 'todos/todoAdded': {
      // We need to return a new state object
      return {
        // that has all the existing state data
        ...state,
        // but has a new array for the `todos` field
        todos: [
          // with all of the old todos
          ...state.todos,
          // and the new todo object
          {
            // Use an auto-incrementing numeric ID for this example
            id: nextTodoId(state.todos),
            text: action.payload,
            completed: false
          }
        ]
      }
    }
    default:
      // If this reducer doesn't recognize the action type, or doesn't
      // care about this specific action, return the existing state unchanged
      return state
  }
}

ActionのTypeを参照して、処理を分岐させてる。
また、Action.payloadを参照することで、Componentが引数として指定した任意のデータも参照することができる。

Reducerは、現在のstateとActionをもとに新たなstateを計算し、そのstateをStoreに反映させる。

image.png

実際に新たなstateを反映しているのは、下記のreturn文。

todosReducer.js
case 'todos/todoAdded': {
      // We need to return a new state object
      return {
        // that has all the existing state data
        ...state,
        // but has a new array for the `todos` field
        todos: [
          // with all of the old todos
          ...state.todos,
          // and the new todo object
          {
            // Use an auto-incrementing numeric ID for this example
            id: nextTodoId(state.todos),
            text: action.payload,
            completed: false
          }
        ]

補足)ContainerとComponent

Containerの役割はStoreとComponentの橋渡し。
しかし、ComponentはContainerがいなくても、直接Storeのdispatch()を利用することができる。

なぜContainerが必要か?

ComponentがReduxに依存してしまう。

  • Componentが状態やロジックを保持することになり、テストがしにくくなる。
  • Redux以外のデータソースから、Componentに対して値を受け渡しにくくなる。

ちなみに、Containerは下記のように、Componentに対してStateとdispatch()を提供する。

container.js
function mapStateToProps (state) {
  return {...state}; 
}

function mapDispatchToProps(dispatch) {
  return {
    todoAdded: () => dispatch(todoAdded()),
  };
}


export default connect(mapStateToProps, mapDispatchToProps)(Component);

※上記のconnect()は古いらしく、hook全盛期のReactではもっとシンプルに書けるそうです。
https://qiita.com/seya/items/700184c0d4a52bc0d32b

間違いあればご指摘ください!

追記

image.png

Container内で、dispatch()をカスタマイズ(任意のActionを定義)する関数を作成し、Componentはその関数に対してPayloadを投げてあげる。みたいな構成もあるらしい。

(そっちの方がAction単位でテストできるし、Containerの肥大化も避けれるしよさげ??)

参考

https://redux.js.org/tutorials/essentials/part-1-overview-concepts
https://qiita.com/mpyw/items/a816c6380219b1d5a3bf#action-%E3%81%8A%E3%82%88%E3%81%B3-action-creator
https://qiita.com/seya/items/700184c0d4a52bc0d32b
https://react-redux.js.org/api/hooks

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