1. ホーム
  2. javascript

[解決済み] Redux Reducerでストアの初期状態を読み込む

2023-05-10 20:26:34

質問

Reduxアプリの初期状態は、2つの方法で設定することができます。

ストアに初期状態を渡す場合、ストアからその状態を読み取り、リデューサの最初の引数にするにはどうすればよいでしょうか。

どのように解決するのですか?

TL;DR

なし combineReducers() などの手動のコードで initialState は常に state = ... が勝つので、reducerでは state はリデューサに渡される initialState であり undefined であるため、ES6の引数構文はこの場合適用されません。

とは combineReducers() では、より微妙な挙動になります。で状態が指定されているリデューサは initialState で指定されたリデューサは、その state . 他のレデューサは undefined となり、そのため にフォールバックすることになります。 state = ... というデフォルトの引数が指定されます。

一般的には initialState はリデューサによって指定された状態よりも優先されます。これにより、リデューサは意味のある初期データを指定することができます リデューサにとって をデフォルトの引数として指定できますが、ストアを永続的なストレージやサーバーからハイドレートするときに、既存のデータを (完全にまたは部分的に) ロードすることもできます。

まず、reducerを1つ持っている場合を考えてみましょう。

を使わないとします。 combineReducers() .

そうすると、reducerは以下のようになります。

function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT': return state + 1;
  case 'DECREMENT': return state - 1;
  default: return state;
  }
}

では、これでお店を作るとしましょう。

import { createStore } from 'redux';
let store = createStore(counter);
console.log(store.getState()); // 0

初期状態は0です。なぜか? なぜなら、第二引数の createStoreundefined . これは state に渡されるものです。Reduxが初期化されるとき、状態を満たすために「ダミー」のアクションがディスパッチされます。そのため、あなたの counter で呼び出されたリデューサは state と同じ undefined . これはまさにデフォルトの引数を「発動」させるケースです。 したがって state は現在 0 は、デフォルトの state の値 ( state = 0 ). この状態( 0 )が返されます。

別のシナリオを考えてみましょう。

import { createStore } from 'redux';
let store = createStore(counter, 42);
console.log(store.getState()); // 42

なぜ 42 でなく 0 でなく、今回は?なぜなら createStore が呼び出されたからです。 42 を第二引数として呼び出す。この引数は state になり、ダミーアクションと一緒にreducerに渡されます。 今回は state は未定義ではなく、(それは 42 !)なので、ES6 のデフォルトの引数シンタックスは何の効果もありません。 state42 であり、かつ 42 がreducerから返されます。


それでは、例えば combineReducers() .

レデューサーが2つありますね。

function a(state = 'lol', action) {
  return state;
}

function b(state = 'wat', action) {
  return state;
}

で生成されたリデューサは combineReducers({ a, b }) はこのようになります。

// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
  return {
    a: a(state.a, action),
    b: b(state.b, action)
  };
}

もし createStore を呼び出すと initialState を初期化します。 state から {} . したがって state.a となり state.b となるのは undefined を呼び出すまでに ab のレデューサーです。 ともに ab レジューサーは undefined として その state 引数を指定し、デフォルトの state の値が指定されていれば、それが返されます。 このように、結合されたreducerが返す { a: 'lol', b: 'wat' } の状態オブジェクトを最初の起動時に返します。

import { createStore } from 'redux';
let store = createStore(combined);
console.log(store.getState()); // { a: 'lol', b: 'wat' }

別のシナリオを考えてみましょう。

import { createStore } from 'redux';
let store = createStore(combined, { a: 'horse' });
console.log(store.getState()); // { a: 'horse', b: 'wat' }

今度は initialState を引数として createStore() . 結合されたリデューサから返される状態 を結合する に指定した初期状態を a レデューサに指定した初期状態と 'wat' というデフォルトの引数が指定されています。 b reducerは自分自身を選んだ。

結合されたreducerが何をするのか思い出してみましょう。

// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
  return {
    a: a(state.a, action),
    b: b(state.b, action)
  };
}

この場合 state にフォールバックしないように指定されました。 {} . を持つオブジェクトでした。 a と等しいフィールドを持つオブジェクトでした。 'horse' と同じですが b フィールドはありません。このため a を受け取ったレデューサは 'horse' をその state として、喜んで返しましたが b を受け取ったレジューサーは undefined をその state として返され、その結果 その考え のデフォルトの state (この例では 'wat' ). このようにして { a: 'horse', b: 'wat' } を返します。


まとめると、Reduxの規約を守り、リデューサーが呼び出されたときに初期状態を undefinedstate 引数として指定します(最も簡単な実装は state ES6のデフォルトの引数値です)、複合リデューサーのための素敵な便利な振る舞いを手に入れることになります。 の対応する値を好むようになります。 initialState オブジェクトに渡す createStore() 関数に渡すことができますが、何も渡さなかったり、 対応するフィールドが設定されていない場合は、デフォルトの state の引数が代わりに選ばれます。 このアプローチは、既存のデータの初期化と水増しの両方を提供しつつ、データが保存されていない場合は個々のリデューサに状態をリセットさせるので、うまく機能します。もちろん、このパターンを再帰的に適用することも可能で、その場合は combineReducers() を使うことができるので、このパターンを再帰的に適用することもできますし、リデューサを呼び出して状態ツリーの関連部分を与えることで、リデューサを手動で構成することもできます。