useSelector
第1引数はselector :: Store => State
selectorは純粋関数である必要がある
差分がある場合はFCを再描画させる
selectorの実行タイミング
FCが再描画された際
actionがdispatchされた場合
selectorは実行されるが、これによってFCの再描画を引き起こすかどうかは、selectorの内容に依る
以下2つの違い
const state = useSelector(s => s);
Storeそのもの(Store内のどれか1つでも)が更新されたらstateは変更され、それを使っているコンポーネントは再描画される
const b = useSelector(s => s.b);
s.bのみを見ており、s.bが更新された場合のみ、bが更新され、そのコンポーネントが再描画される
裏でs.aなどが更新されてもbは更新されない
第2引数はshallowEq :: (prev, next) => boolean
memo化するために使う
第2引数を省略するとmemo化されない
というか、===比較で判断する
selectorそのものの比較が===であり、
厳しすぎるので第2引数でshallowEqを使って緩くしている
厳しすぎるというのは、無駄に再描画されてしまう可能性がある、という意味
つまり、selectorの返り値がobjectの場合のみの話をしている
primitiveな値の場合は第2引数を設定する意味がない(挙動に差異がない)
ex.
オリジナルの関数
前提として
「useSelectorの実行」と「コンポーネントの再描画」が同じ回数だけ行われるわけではないということを知っておく必要がある
useSelectorの実行タイミング
actionがdispatchされたら。
アプリ内の一つのactionがdispatchされたら、
アプリ内のmountされているComponent内の全てのselectorが実行される
同様にequalityFnも毎度実行される
useSelectorによるコンポーネントの再描画タイミング
equalityFnがある場合は、これがfalseを返した場合
1Component内に複数のselectorがある場合は、1回の再描画に抑えられる
つまりuseSelectorによる再描画は0回 or 1回
故に、任意のselectorの設計の如何によってアプリ全体のパフォーマンスに影響を及ぼす
普通に書くとあり得ないが、仮にselectorにめちゃくちゃ重い処理をさせると、アプリ全体がめちゃくちゃ重くなる
ちなみに最終のequalityFnのtrue/falseによってselectorの呼び出し回数は変わる
最終のequalityFnがtrueのとき
selector→equalityFn→再描画なし
最終のequalityFnがfalseのとき
selector→equalityFn→selector→再描画
あまり気にする必要ないけど。
この記事の「selector や equalityFn はいつ実行されるのか」以下に書いている selectorのアンチパターン
selector内でmapなどのmethodを使う
mapを使うと必ず新しいobjectが作られるので===は常にfalseになる
code:ts
const a = hoge.map(s => s);
console.log(a === hoge); // false
つまり第2引数を省略した場合は100%の確率で再描画を引き起こす
code:ts
const hoge = useSelector(s => s.hoge.map(h => h))
filterなども同様
これは第2引数を設定することで回避できる。しかし、それでもまだ問題がある。
さらに、上にも書いた通り、selectorはアプリ内で頻繁に実行される
memo化云々は関係なくselectorを煩雑にしてはいけない
この無駄なmap処理がdispatchの度に実行される
再描画されるかどうかは兎も角、実行されまくる
なので第2引数の有無を問わずselector内でこういう処理を書くべきではない
同様にequalityFnも毎度実行されるのでこの中でmapをスべきではない #?? propsに依存するときは気をつけないといけない
#WIP あんまわかってないmrsekut.icon useSelectorがpropに依存する、ってどういうとき?
state.todos[props.id].nameこういうやつか
selector内にpropsを使う場合
code:ts
const TodoListItem = props => {
const todo = useSelector(state => state.todosprops.id) return <div>{todo.text}</div>
}
型の付け方
code:ts
import 'react-redux'
declare module 'react-redux' {
interface DefaultRootState extends ReduxState {}
}
参考
以下の書き方のどちらが良いのか
storeがこういうもののとき
code:ts
type Store = {
b: {
name: string;
age: number;
};
};
name, ageを使いたいときに
code:ts
const { name, age } = useSelector(s => s.b);
code:ts
const name = useSelector(s => s.b.name);
const age = useSelector(s => s.b.age);
やっぱ後者なのかな
参考