React Memo

React.memoは高階コンポーネント(Higher Order Component, HOC)であり、関数コンポーネントのパフォーマンスを最適化するために使用されます。コンポーネントのレンダリング出力をメモ化し、props が変化していない場合は再レンダリングをスキップすることで、パフォーマンスを向上させます。以下はReact.memoの詳細な紹介と使用方法です。

1. 基本的な使い方

React.memoの基本的な使い方は、関数コンポーネントを引数としてReact.memo、メモ化されたコンポーネントを返すことです。

例

import React from 'react';
import ReactDOM from 'react-dom';

const MyComponent = React.memo((props) => {
  console.log('Rendering MyComponent');
  return <div>{props.text}</div>;
});

const App = () => {
  const [count, setCount] = React.useState(0);
  const [text, setText] = React.useState('Hello, world!');

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <MyComponent text={text} />
    </div>
  );
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

この例では、たとえcount状態が変化しても、MyComponentも再レンダリングされません。そのpropsが変化していないからです。

2. カスタム比較関数を使用する

デフォルトでは、React.memo前後を比較するだけでprops、変化がなければ再レンダリングされません。カスタム比較関数を渡すことで、再レンダリングのロジックをより正確に制御できます。

カスタム比較関数のシグネチャ:

function areEqual(prevProps, nextProps) {
  // 返回 true 表示相等,不需要重新渲染
  // 返回 false 表示不相等,需要重新渲染
}

例

import React from 'react';
import ReactDOM from 'react-dom';

const MyComponent = React.memo((props) => {
  console.log('Rendering MyComponent');
  return <div>{props.text}</div>;
}, (prevProps, nextProps) => {
  return prevProps.text === nextProps.text;
});

const App = () => {
  const [count, setCount] = React.useState(0);
  const [text, setText] = React.useState('Hello, world!');

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <MyComponent text={text} />
    </div>
  );
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

この例では、カスタム比較関数はtextが変化したかどうかをチェックします。もしtextが変化していなければ、MyComponentは再レンダリングされません。

3. 実際の適用シーン

React.memo以下のようなシーンに非常に適しています:

  • 静的データの表示:コンポーネントが受け取るpropsがほとんど変化しないが、コンポーネント自体は複雑で、再レンダリングのコストが高い。
  • パフォーマンス最適化:大きなリストやテーブルでは、各項目が独立したコンポーネントであり、使用することでReact.memo不要な再レンダリングを回避できます。
  • 深い等価チェックの回避:カスタム比較関数は深い等価チェックを回避できます。特にpropsが大量のデータを含む場合に有効です。

4. 注意事項

  • 浅い比較:デフォルトでは、React.memoは浅い比較を行います。つまり、propsの第1レベルの内容のみを比較し、ネストされたオブジェクトにはカスタム比較関数が必要です。
  • 状態とコンテキスト:React.memo注目するのはpropsの変化であり、コンポーネント内部の状態とコンテキストの変化は再レンダリングをトリガーしません。

5. とuseMemoとuseCallbackの違い

  • React.memo:コンポーネント全体をメモ化して、コンポーネントのレンダリングを最適化します。
  • useMemo:関数コンポーネント内部の値や計算結果をメモ化します。
  • useCallback:関数コンポーネント内部のコールバック関数をメモ化し、不要な再生成を回避します。

例

import React, { useState, useMemo, useCallback } from 'react';
import ReactDOM from 'react-dom';

const ChildComponent = React.memo(({ onClick, count }) => {
  console.log('Rendering ChildComponent');
  return <button onClick={onClick}>Count: {count}</button>;
});

const App = () => {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  const doubledCount = useMemo(() => count * 2, [count]);

  return (
    <div>
      <p>Doubled Count: {doubledCount}</p>
      <ChildComponent onClick={increment} count={count} />
    </div>
  );
};

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

この例では、useCallbackとuseMemoは関数と計算結果の最適化に使用され、React.memoは子コンポーネントのレンダリングの最適化に使用されます。

まとめ

React.memoは強力なツールであり、関数コンポーネントのパフォーマンスを効果的に向上させ、不要な再レンダリングを回避できます。合理的に使用し、React.memoカスタム比較関数を組み合わせることで、アプリケーションのロジックに影響を与えずに、アプリケーションのパフォーマンスを大幅に最適化できます。

その他の拡張