🗂

Reactアプリのチューニング

2023/04/24に公開

Reactアプリのチューニング

  • (一回のごとの)レンダリングを軽くする
  • 無駄な再レンダリングを抑制(スキップ)する

そもそもレンダリングってなんだ?


画像出典: https://www.telerik.com/blogs/understand-how-rendering-works-react

  • render関数の実行
    • FCであればFCの実行そのもの
    • 仮想DOMを組み立てる処理
  • reactの内部処理
  • 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