💻
ReactのuseStateの引数を無名関数にした方がいいとき
結論
-
value
みたいな変数を参照して何らかの判定を行う時:無名関数[foo, changeFoo] = useState<boolean>(() => value === 'sample')
-
1
とかtrue
みたいなプリミティブな値の時:そのまま渡す[foo, changeFoo] = useState<boolean>(true)
なんで?
- パフォーマンスが改善されるから
- 無名関数にすると、コンポーネントがマウントされてから一度だけ無名関数が実行される
- 変数を参照しているのに無名関数にしていない場合、コンポーネントが再レンダリングされる度に引数の判定が何度も実行される!
- プリミティブな値(
1
とかtrue
)を渡す場合は判定不要なので、そのまま渡せばOK
修正の具体例
- 修正前と修正後で挙動そのものは変わらないが、パフォーマンスが改善される
Before
- コンポーネントが再レンダリングされる度に
visualRef!== undefined
の判定が実行される - 機能としては問題ないが、パフォーマンスに改善の余地あり
const [isTransparent, changeIsTransparent] = useState<boolean>(visualRef!== undefined);
After
-
useState
の引数を無名関数化したことにより、コンポーネントがマウントされた時に一度だけvisualRef !== undefined
の判定が実行される - 無駄なレンダリングコストを削減できた!
const [isTransparent, changeIsTransparent] = useState<boolean>(() => visualRef !== undefined);
余談1: useMemoで初期値を設定するのと同じ
- ちなみに、↑は以下の内容と等価
-
useMemo
でメモ化して、不要な再計算が行われないようにしている - これなら
useState
+ 無名関数にリファクタした方が可読性が高い
-
const hasVisualRef = useMemo(() => visualRef !== undefined);
const [isTransparent, changeIsTransparent] = useState<boolean>(hasVisualRef);
useState
の初期値が複雑な場合、無名関数化すると可読性が高まる
余談2: - 引数に応じて初期値を変える場合、こんな風に無名関数化すると読みやすい
- この場合はレンダリングコストというより、コードをスッキリさせるために無名関数を使うとOK
const [status, changeStatus] = useState<'下書き' | '公開' | 'その他'>(() => {
switch (status) {
case 1:
return '下書き';
case 2:
return '公開';
default:
return 'その他';
}
});
さいごに
「期待する動作は同じだが、パフォーマンスに改善の余地あり」という修正、気づくのが難しい...
Discussion