🐈

Reduxのデータの流れ

2023/03/21に公開

はじめに

最近youtubeでのreduxチュートリアルを一周したのですが、まだ理解が及ばないところが多いので、復習としてまとめていきます。

※取り組んだチュートリアルはこちら

https://www.youtube.com/watch?v=FBMA34gUsgw&t=1s

Reduxとは

アプリケーションの状態(React.jsでのstate)を管理するライブラリ。
fluxフローという設計思想を元に作られています。

stateを一元管理できるので、各コンポーネントから直接データを参照・取得することができます。

fluxフローとは

アプリの設計思想の1つで、下記図のようにデータが常に1方向に流れる特徴があります。
View → Action → Dispatcher → Store → View・・・ の流れでユーザーの操作に伴い、画面が更新されていく仕組みです。
※Reduxはあくまでfluxフローを元に作られたものなので、fluxフローとはいくつか違う点(Dispatcherが無いなど)があります。

各項目について

View : ブラウザに表示されるアプリの各component(厳密にはブラウザに表示されるstoreのデータ)ユーザーが変更することで次のActionを飛ばすトリガーになる
Action : アプリの状態を変更する指示の内容
Dispatcher:Actionの内容をStoreに伝達する
Store: アプリの状態を保持しており、変更指示を受けたら該当データを更新する

Reduxのデータの流れについて

上記のfluxフローを踏まえて、reduxのデータの流れを見てみます。
今回はサインイン画面でサインインする場合を想定しました。
※あくまでデータの流れを理解するのが目的のため、実装方法やディレクトリ構造は簡略化しています。

流れだけ先に図にすると下記のようになります。

1.画面からユーザーが操作を実行

サインイン画面でユーザーがアカウント情報を入力&サインインボタンをクリック
関数名などは後述しますが、ひとまずボタンのDOMは下記のようになっています。

app.js
<button type="button" onClick={()=>dispatch(signInAction)}>サインイン</button>

2.クリックをトリガーにActionが発行される

Actionはアプリの状態を変更する指示の内容を必ずプレーンなオブジェクトで返します。
返すだけなので、Action自体に何かを変更する機能はありません。

中身は下記のイメージです。

Action.js
export const SIGN_IN = 'SIGN_IN';
export const signInAction = () => {
    return {
        type: SIGN_IN,
        payload: {
            isSignedIn: true
        }
    }
}

Type:このActionを識別するための任意の文字列(今回はそのままSIGN_IN)
Payload:このActionを実行するために必要な任意のデータ(サインインさせたいので、trueのisSignInフラグを渡します。)

3.DispatchがActionを運ぶ

1のDOMクリック時に発火する関数内にsignInActionを引数に持ったdispatchがいます。
このdispatchによってactionが次のreducerへ渡されます。

4.ActionがReducerに渡される

Actionの内容を元に、変更された結果のstateを返してくれます。

下記では2種類の変数がありますが、上のinitialStateがデフォルトのstateを定義しています。デフォルトではログインされていない状態なので、フラグをfalseで持っておきます。

次にusersReducerは初期状態と先程のactionを受け、対応する状態を返してくれる関数です。
switch分で分岐されているので、SIGN_INのactionを受けたら対応のオブジェクトを返します。

Reducer.js
import * as Actions from './Action';

// デフォルトのstate(ログインしていない状態)
const initialState = {
    isSignedIn: false
}

export const usersReducer = (state = initialState, action) =>  {
    switch (action.type) {
        case Actions.SIGN_IN :
            return  {
            ...state,
            ...action.payload
            }
    default:
        return state
    }
}

ちなみにオブジェクトがスプレッド構文で書かれているのは、初期のstateとactionの値とまとめるためです。
今回はisSignInしかキーがないので、まとめられた結果、initialState側のisSignedInがtrueに上書きされます。

5.ReducerによりStore内のstateが変更される

ストアの中身は下記のイメージです。
configureStoreモジュールをimportし、定義したreducerを下記のように渡して登録しておくと、storeもReducerでの変更を受け取ってくれます。

Store.js
import { configureStore } from '@reduxjs/toolkit';
import { usersReducer } from './Reducer.js';

export const store = configureStore({
    reducer:{
        users: usersReducer,
    }
})

6.view側にstateが共有される

これで変更の更新はstoreに伝わり、最後view側に更新が伝わります。
下記ソース内のProviderはラップしたコンポーネントにstoreの情報を渡す役割があります。

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './Store';
import App from './App';

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

参考

Discussion