Yanonoblog!

こつこつと

useSelectorとImmerの概要

はじめに

Reduxの公式ドキュメントに沿って概要を押さえつつ、気になった部分を深掘って整理しています。

本記事では 、Reduxのサンプルアプリを通してuseSelectorとImmerの概要についてまとめていきます。

Selector

コンポーネントセレクター関数を通じてストアからデータを取得します。直接ストアのデータをインポートすることはできません。

セレクター関数はストアの現在の状態を引数として受け取り、必要なデータを返します。

下記の例では、状態から値を選択するためのセレクター関数を定義しています。

export const selectCount = state => state.counter.value

Reduxストアへのアクセスがあれば、現在のカウンター値を以下のように取得することができます

const count = selectCount(store.getState())
console.log(count)
// 0

これは、Reduxのストアから現在の状態を直接取得し、それをselectCountセレクター関数に渡すことでカウンターの値を取得します。

この方式ではReduxストアの状態が変更されたときに自動的に再計算されないため、useSelectorを使用します。

useSelector

useSelectorでは、渡されたセレクター関数を使用してストアの状態を取得し、その結果を返します。

const count = useSelector(selectCount)

さらに、useSelectorはReactのレンダリングと結びついており、関連するストアの状態が変更されると自動的にセレクター関数を再計算し、コンポーネントを再レンダリングします。

これにより、Reactコンポーネントは常に最新の状態を表示することが保証されます。

また、既にエクスポートされたセレクターだけを使用する必要はありません。

例えばuseSelectorへのインライン引数としてセレクター関数を書くこともできます。

const countPlusTwo = useSelector(state => state.counter.value + 2)

アクションがディスパッチされ、Reduxストアが更新されたときには、useSelectorは私たちのセレクター関数を再度実行します。セレクターが前回と異なる値を返した場合、useSelectorは新しい値でコンポーネントが再レンダリングされることを保証します。

Stateの中身

リデューサーは state を引数にとり、その状態を変更します。

スライスのreducerで引数に取ったstateを出力するとProxyオブジェクトの出力が確認できました。

これが気になったため調べました。

// reducerでstateをconsole.log();で出力
export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      console.log(state);
...略

// 出力結果
Proxy(Object) {i: 0, A: {}, P: false, I: false, R: {}, …}

Redux ToolkitはImmerというライブラリを内部で使用しており、そのおかげで直接状態を変更することができます。

オブジェクトに対する変更は、通常のJavaScriptオブジェクトではなくImmerの特殊なProxyオブジェクトで行われます。

Redux Toolkitが追跡して不変性を保証します。

そのため、 console.log(state); が表示するのは、通常のJavaScriptオブジェクトではなくImmerが生成する特殊なProxyオブジェクトです。

Immer

Immerは、JavaScriptのオブジェクトや配列の不変更新(変更されることなく新しい状態を生成する更新)を簡単に扱うためのライブラリです。

開発者がオブジェクトや配列を直接変更することができるかのようにコードを書くことを可能にします。

実際には元のデータは変更されず、変更を反映した新しいオブジェクトや配列が生成されます。

以下のようなJavaScriptのオブジェクトがあるとします。

let state = {
  value: 1
};

このオブジェクトの value プロパティを更新するためには、通常は新しいオブジェクトを作成し、そのプロパティを設定する必要があります。

state = {
  ...state,
  value: 2
};

Immerを使用すると、オブジェクトを直接更新するかのようにコードを書くことができます。

state = produce(state, draftState => {
  draftState.value = 2;
});

produce はImmerの主要なAPIで、現在の状態(state)と「プロデューサー関数」(ここでは draftState => { draftState.value = 2; })を引数にとります。

この関数内で、draftState(現在の状態のドラフト)を直接変更することができます。

Immerはこれらの変更を追跡し、新しいオブジェクトを作成します。

Redux Toolkitは内部的にImmerを使用しており、それによりリデューサー関数内で状態を直接変更することが可能となっています。

参考

続く…

コメント

本記事の内容は以上になります!

書籍の続きのアウトプットも随時更新したいと思います。


プログラミングスクールのご紹介 (卒業生より)

お世話になったプログラミングスクールであるRUNTEQです♪

https://runteq.jp/r/ohtFwbjW

こちらのリンクを経由すると1万円引きになります。

RUNTEQを通じて開発学習の末、受託開発企業をご紹介いただき、現在も双方とご縁があります。

もし、興味がありましたらお気軽にコメントか、TwitterのDMでお声掛けください。

https://twitter.com/outputky