🗂
Reactアプリのチューニング
Reactアプリのチューニング
- (一回のごとの)レンダリングを軽くする
- 無駄な再レンダリングを抑制(スキップ)する
そもそもレンダリングってなんだ?
画像出典: https://www.telerik.com/blogs/understand-how-rendering-works-react
- render関数の実行
- FCであればFCの実行そのもの
- 仮想DOMを組み立てる処理
- reactの内部処理
- 仮想DOMの差分検出処理(reconciliation)
- DOMへの反映
- コミットフェーズ
- DOMの描画処理
- ブラウザのレンダリング処理
memo化のよるレンダリングの抑制
const NameLabel = ({name}) => <label>{name}</label>
const NameLabel = memo(({name}) => <label>{name}</label>)
全てのpropsの値が変わっていなかったらレンダリングをスキップ
前回の値と新しい値をObject.isで比較(===
での比較とほぼ同義)
(+0, -0, NaNの扱いが違うだけ)
memo化されたコンポーネントがpropsでオブジェクト(配列・関数)を受け取る場合注意が必要
{a:1} === {a:1} // false
[1,2,3] === [1,2,3] // false
<NameLabels names={["hoge", "fuga"]} />
<Button onClick={() => setValue(true)} />
const names = useMemo(() => ["hoge", "fuga"], [])
<NameLabels names={names} />
const onClick = useCallback(() => setValue(true), [])
<Button onClick={onClick} />
// <Button onClick={() => onClick()} /> useCallbackが無意味になる!
const onClick = useCallback(() => setValue(hoge), [hoge])
複数のpropsを受け取る場合、1つでもmemo化を忘れると全ての努力が無駄になる
depsを過不足指定しないとbugになったり、memo化が不完全になる
コンポーネントツリーのなるべく根っこに近いコンポーネントをmemo化したい
ただしchildrenもpropsに含まれるためチェックの対象になる点に注意!
reduxにおけるレンダリングの抑制
useSelectorで取得する値の範囲をなるべく絞る
const messages = useSelector(state => state.messages)
const firstMessageID = messages[0]?.id
const firstMessageID = useSelector(state => state.messages[0]?.id)
オブジェクト・配列操作をする場合は例外
useSelector(state => state.messages.filter(e => e.deleted))
useRef
レンダリングに影響を与えない状態を保持する際にuseStateではなくuseRefを使う
Discussion