はじめに
おはようございます、Tunです。
今回は、レンダリングの最適化のついて記述していきたいと思います。
再レンダリングの条件
- 再レンダリングが起きる条件
- Stateが更新されたコンポーネント
- Propsが変更されたコンポーネント
- 再レンダリングされたコンポーネント配下のコンポーネント全て
今まで記述してきたように、state
とprops
はコンポーネントの状態や、その値によってレンダリング内容を決定するものですので、再レンダリングが起きるのは想像できるかと思います。
3つ目に関しては、想像しにくい挙動ですが、以下のようなフォルダの構成を考えてみて下さい。
📁[project]
└📁public
└index.html
└📁src
└📁components
└Clild1.jsx
└Child2.jsx
└Child3.jsx
└Child4.jsx
└App.jsx
└index.html
└package.json
これらのコンポーネントの階層構造は、App.jsx
の下層にChild1.jsx
とChild4.jsx
、そのChild1.jsx
の下層にもChild2.jsx
とChild3.jsx
があるとします。すると、App.jsx
のstate
を更新した場合、全てのコンポーネントが再レンダリングされてしまいます。
ということは、再レンダリングされたコンポーネント配下のコンポーネントが全て再レンダリングされてしまうということです。
React.memo
Reactにおいて、コンポーネント、変数、関数などの再レンダリング時の制御をするにはメモ化を行います。
メモ化とは、前回の処理結果を保持しておいてパフォーマンスを向上させます。必要な時のみ再計算することで不要な処理を省くことができます。
const MyComponent = memo(() => {});
たったこれだけの手間でprppsに変更がない限り再レンリングされないようになりました。
React.useCallback
関数をprops
に渡すときにコンポーネントをメモ化していても再レンダリングされてしまう原因は関数の再生成です。詳しく言うと、再レンダリング等でコードが実行されている度、常に新しい関数が再生成されているということになります。props
を受け取ると、propsが変化したと判定され、再レンダリングされます。この事象を防ぐために、関数のメモ化を行う必要があります。
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},[a, b],
);
useCallback
は、第1引数に関数、第2引数に依存配列を指定します。この指定した関数は、依存配列の要素のいずれかが変化した場合のみ変化し、不要なレンダーを避けます。依存配列が空の場合は、最初に作成されたものが使いまわされます。
また、次でも記述しますが、useCallbnack(関数, 依存配列)
はuseMemo(() => 関数, 依存配列)
と等価です。
React.useMemo
const memoizedValue = useMemo(
() => computeExpesiveValue(a, b),[a, b]
);
useMemo
は依存配列の要素のいずれかが変化した場合のみメモ化された値を再計算します。
また、useMemo
に渡した関数はレンダー中に実行されます。レンダー中に通常やらないことは、この関数内で行うのはやめましょう。
useMemoはパフォーマンス最適化のために使うものであり、意味上の保証があるものだと考えないようにしましょう
⇒例えば、画面外のコンポーネント用のメモリを解放するために、将来的にReactはメモ化された値を忘れるようにする可能性があります。useMemo
なしでも動作するコードを書き、パフォーマンス最適化の観点で加えるようにしましょう
次回はグローバルなState管理です