はじめに
Reactを学び始めてから2ヶ月経ったころ、ずっとおまじないと言われてきたReduxをついに教わりました。
ソースコードを見ていると、useSelector
でReduxストアからデータを取得するとき、最後におまけのような見慣れないshallowEqual
という単語が書かれていました。
先輩に聞いてみると、意味自体はシンプルなのですが前提知識がないと理解が難しい内容だったのでまとめてみます。
React/JavaSctipt/TypeScriptを学び始めたばかりの、プログラミング初心者の方の参考になれば嬉しいです。
shallowEqualとは?
前提知識を整理する前に、先に結論を書きます。
-
shallowEqual
はReactのReduxライブラリが用意している関数 - オブジェクトの浅い比較を行う
- コンポーネントの再レンダリングを防ぐために便利
shallowEqual
はReactのReduxライブラリが用意している関数(ユーティリティ関数)でオブジェクトの浅い比較を行います。この浅い比較について、JavaScriptにおけるオブジェクトの比較について知らないと理解が難しいので、先にこちらを解説します。
JavaScriptにおけるオブジェクトの比較
JavaScriptにおいて、オブジェクトの比較は参照の比較をします。参照の比較とは、オブジェクトが保存されるメモリの番地(住所)を比較するということです。オブジェクトの中身を見て比較をしているわけではありません。
新しくオブジェクトを作るとメモリのどこかに保存されます。次にオブジェクトを作ると、また新しい場所に保存されます。そのオブジェクト同士の参照を比較すると、保存場所が異なるため、異なるオブジェクトと判定されます。
よって、JavaScriptでは厳密等価演算子を使って比較すると、以下のような中身が全く同じオブジェクトでもfalse
と判定されます。
const object1 = {
name: "hello",
};
const object2 = {
name: "hello",
};
console.log(object1 === object1); // true
console.log(object1 === object2); // false
全く同じ内容のオブジェクトなのに異なるオブジェクトと判定されるのは、直感と反しますよね。ポイントは、オブジェクトの中身まで確認していない、あくまで保存場所を比較している点です。
この厳密等価演算子を使ったオブジェクトの参照が同じかどうかだけを比較する方法を、浅い比較(Shallow Comparison) と呼びます。
shallowEqualとは?
これを踏まえた上であらためてshallowEqual
について考えます。
shallowEqual
はオブジェクトの浅い比較を行います。前項で説明した通り、通常の厳密等価演算子で比較をする場合、オブジェクトの中身が同じでも別のオブジェクトとみなされますが、shallowEqual
を使用することで、オブジェクトの中身が一致する場合にtrue
を返します。
const obj1 = { name: "hello" };
const obj2 = { name: "hello" };
console.log(obj1 === obj2); // false
console.log(shallowEqual(obj1, obj2)); // true
shallowEqualのメリット
shallowEqual
を使うことで、無駄な再レンダリングを回避できます。
通常、useSelector
は Reduxストアに変更があると、それを検知してコンポーネントを再レンダリングします。ただし、全ての変更がそのコンポーネントにとって必要なわけではありません。
Reduxストアは大きな1つのオブジェクトで構成されており、reducerで新しいオブジェクトを作成すると、中身が同じであっても参照が異なります。これが、不要な再レンダリングを引き起こす原因です。
そこでshallowEqual
を使用すると、中身が同じであれば同じオブジェクトとみなされるので無駄な再レンダリングを防ぐことができます。
shallowEqualの使い方
useSelector
の第2引数(比較関数)にshallowEqual
を直接指定します。これにより、selectorReturningObject
が返すオブジェクトの比較にshallowEqual
が適用されます。
import { shallowEqual, useSelector } from 'react-redux'
// 直接第2引数に渡す
const selectedData = useSelector(selectorReturningObject, shallowEqual)
shallowEqualの注意点と使い所
-
shallowEqual
はオブジェクトの1段階目しか見ないので、ネストしたオブジェクトには使えません。階層が深いものに使用したい場合は、自分で作りましょう。 - 基本的に
useSelector
でオブジェクトを参照するときは無駄な再レンダリングを防ぐため、shallowEqual
で比較しましょう。 - プリミティブ型(数値、文字列、真偽値など)の場合は、通常の厳密等価演算子で比較されるため、
shallowEqual
で比較する必要はありません。 - 配列に対しては効果がないので注意。
終わりに
Reduxを使用する際に、useSelector
とshallowEqual
を組み合わせることで、不要なコンポーネントの再レンダリングを防ぐことができ、パフォーマンスの向上につながります。
useSelector
でデータを取得するときは、パフォーマンスも意識して書けるようにしましょう!