はじめに
おはようございます、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管理です![]()