React.memo/useMemo/useCallbackを使ってサイトを最適化

React開発においては、不要なレンダリングを防ぐためにReactで用意されたHooksを使用してメモ化を意識した開発を行う必要があります。
特に実際の開発においては以下の3つのAPIをたびたび使用します。

  • React.memo
  • useMemo
  • useCallback

本記事ではこの3つのAPIについて書いていていきます。
Reactのメモ化についてすでに熟知している人は読む必要のない記事です。

メモ化とは?

まず初めにここでいうメモ化についてちゃんと定義しておきます。
メモ化はプログラミングでよく知られた概念であり、関数呼び出しの結果をキャッシュし、キャッシュを再利用することによってプログラムを高速化することを目指しています。
Reactにおいては、コンポーネントのレンダリングやプロセスを高速化させることを目的としたパフォーマンス機能を指します。

実際にReact内部ではrenderメソッドが返したコンポーネントの結果、JSXなどをキャッシュしています。

React.memo

React.memoについてですが、React.memoを使用することでコンポーネントのメモ化を行い、コンポーネントが最初のレンダリング情報をキャッシュします。同様に入力値(propsやstate情報)も記憶して再利用します。

例えば、チャートを扱ったような重たいコンポーネントを処理したい場合などはメモ化させておくことで不要なレンダリングを防ぎパフォーマス改善に繋がります。

実際に使いたいときは以下のようにすれば扱えます

import { memo } 'react';

interface TestProps {
 option: any;
}
// メモ化
const Test = React.memo((props: TestProps) => {
  console.log('render!');
  return <Chart option={props.option} />; // 例:描画が重たいコンポーネント
});

子コンポーネントとして親の外で定義して、親の方で呼ぶことで実行可能
なお、親の内部でメモ化を定義して呼び出しても当たり前ですがメモ化されません。
そのため親の外で定義してください。

useMemo or useCallback?

useMemoとuseCallbackについて掘り下げる前に両者の違いについて理解しましょう。
まず基本としてuseMemoは値を返す、useCallbackは関数を返す
ということを抑えてください。

基本構文は以下

useCallback(fn, deps)
useMemo(() => fn, deps) 

useMemoもuseCallbackも第二引数で渡すdepsの値が変更された時に中身を再評価します。
この二つのHooksは同等の処理として扱われます。
これらの関数を差別化するポイントとしては最初に書いたようにAPIの返す結果の違いです。

useMemo

ではuseMemoはどのような場合に使うのがよいのか?

const memoizedValue = useMemo(() => veryVeryComputeExpensiveValue(a, b), [a, b]);

useMemoは引数にとった値 (上記の場合は a, b) が変わったときにのみ、再計算処理を走らせます。重い処理を不必要に再レンダリング時に毎回実行しないことで、パフォーマンス向上用途に使用できます。

useMemoに渡した関数はレンダリングの最中に実行されます。 APIへのリクエストなどの副作用処理は useMemoではなく、useEffectに記述 しましょう。

useMemoは値だけではなくJSXにも有効でuseMemoを使用してコンポーネントを返すことも可能です。

React.memoとuseMemoの違い

React.memoはHOCでuseMemoはHooksです。
React.memoの場合は、コンポーネント内のpropsが変更されない限り、再レンダリングしないです。
useMemoの場合は、コンポーネント内の関数をラップするために使用できるReact Hook です。依存関係にあるパラメータが変更された場合に再評価することができます。

useCalback

メモ化されたコールバック関数を返します。

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b]
);

React.memoやuseMemoに対してuseCallbackは処理をメモ化できます。
例えば、ボタンを押下した際のクリックのイベントハンドラーなどを記憶しておくと、コンポーネントツリーが再描画された時などのパフォーマンス改善につながります。

また、useEffect内でたびたび使用されるような処理はあらかじめuseCallback等を使用してメモ化させておくとよいでしょう。

参考

関連記事