React memoを活用してパフォーマンスを最適化する方法

React memoとは何か

皆さんはReactを使って画面を作っているときに、必要のない箇所まで再レンダリングされていると感じたことはないでしょうか。 アプリケーションが大きくなるにつれて、こうした無駄な再レンダリングがパフォーマンス低下につながることが多いですね。

そこで登場するのが React memo です。 React 18の環境下でも基本的な考え方は変わらず、コンポーネントが受け取るpropsに変更がなければ前回の出力結果を再利用してくれます。

この仕組みによって、常に変化しない部分を効率的にスキップできるわけです。 ただ、React memoを使えば何でも解決するわけではありません。

React memoが適用されても、propsで渡すオブジェクトや関数の参照が毎回変わっていれば、再レンダリングを抑制できないことがあります。 そのため、React memoは使いどころを見極めることが大切です。

React memoの仕組みと注目ポイント

React memoは関数コンポーネントに対してのみ使用できます。 具体的には、 React.memo (YourComponent) でラップするだけです。

例えば、以下のコードを見てみましょう。

import React from "react";

const DisplayText = React.memo(function DisplayText({ message }) {
  console.log("DisplayText rendered");
  return <p>{message}</p>;
});

export default DisplayText;

ここでは、message が同じ値であれば、再レンダリングをスキップしてくれます。 こうしたメモ化の概念は、実務で複雑な画面を扱う際に便利ですね。

しかし、オブジェクトや配列、関数などのpropsが新たに生成される場合は参照が変わります。 そのため、実際にはReact memoだけで最適化できるケースと、そうでないケースがあることを理解しておく必要があります。

React 18でも、再レンダリングの仕組みそのものが大きく変わるわけではないです。 ただし、並行レンダリング(Concurrent Features)の最適化と組み合わせると、パフォーマンスに影響しやすい部分がより明確になるかもしれません。

React 18では並行レンダリングの概念が導入されましたが、React memo自体の書き方は基本的に変わりません。 不要な再レンダリングを抑える観点でReact memoを使う点はこれまでと同じです。

メリットを引き出す具体例

React memoを使うと、以下のようなメリットを得やすいです。

  • コンポーネント単位で無駄な再レンダリングを防げる
  • 画面全体の描画負荷が下がり、操作レスポンスが向上しやすい
  • 見た目の変更が少ない部分を効率よく処理できる

たとえば、リスト表示において、多数のアイテムを描画するケースを考えてみましょう。 その中で一部のアイテムだけが更新される場面がある場合でも、React memoを適用していなければリスト全体が再レンダリングされてしまいます。

しかし、リストアイテムコンポーネントをReact memo化しておけば、propsが変わったアイテムだけが再描画されるようになります。 これにより、1回あたりのレンダリング負荷を小さく抑えることができるでしょう。

一方で、リストアイテムに受け渡すデータ構造が複雑で、そのたびに新しいオブジェクトが生成されると、結果的にmemo化の効果を得にくいです。 実務では、propsを必要最低限に絞る設計や、useCallbackやuseMemoを組み合わせる工夫も重要ですね。

こうした工夫はReact 18でも同様であり、useCallbackなどで関数の参照を固定することで、本当の変更時だけ再レンダリングされるようになります。 このように、React memo単体だけでなく、関連するReactフックと組み合わせて使うことが効果的です。

実務での活用シーンと注意点

React memoの強みを最大限に活かすには、どのような場面で使うかが重要です。 たとえば、企業の在庫管理システムで大規模なデータテーブルを表示するケースを想像してみましょう。

定期的に更新されるデータと、ほぼ変わらない静的情報をテーブルで混在表示する場合、静的情報を扱う行や列にはReact memoを適用すると良いですね。 そうすることで、頻繁に変化しない情報の再レンダリングを抑制できます。

また、ダッシュボード画面でリアルタイムに数値が更新される部分と、ユーザー情報などが固定される部分を分けてコンポーネント化する場合も、React memoで効率化しやすいです。 ただし、以下のようなケースでは注意しましょう。

1. 過剰なメモ化

全部のコンポーネントにReact memoを付与しすぎる

2. 無効な比較

常にpropsの値が変わるため、浅い比較が有効に働かない

3. 不適切なprops渡し

親コンポーネントが頻繁に再生成する関数やオブジェクトをpropsに渡している

これらの状態だと、React memoの恩恵が薄れがちです。 実務で導入するときは、どのコンポーネントにReact memoを適用するかを見極めることが大切ではないでしょうか。

カスタム比較関数の使いどころ

React memoには、第二引数としてカスタム比較関数を渡せる仕組みがあります。 たとえば、以下のように記述します。

function areEqual(prevProps, nextProps) {
  return prevProps.value === nextProps.value;
}

const CustomCompareComponent = React.memo(
  function CustomCompareComponent({ value }) {
    console.log("Custom comparison active");
    return <div>{value}</div>;
  },
  areEqual
);

このようにすることで、propsの比較方法を独自に定義できるわけです。 しかし、実務ではそこまで細かな調整が不要な場合が多く、カスタム比較を使いすぎるとコードが複雑になりやすいです。

カスタム比較関数を使う場面としては、特定のpropsだけをチェックしたい場合などが挙げられます。 たとえば、大きなオブジェクトを受け取るものの、中で監視したいのは一部のフィールドだけというときです。

ただ、React自体は浅い比較を前提としているため、過剰に深い比較を行うとかえって処理コストが上がるかもしれません。 そのため、カスタム比較が必要かどうかは、実際にパフォーマンスを測定しながら判断するとよいでしょう。

React memoと他の最適化技術との比較

Reactアプリのパフォーマンスを高める方法はReact memoだけではありません。 たとえば、useCallbackuseMemoPureComponent(クラスコンポーネントの場合)などが挙げられます。

useCallback は関数の参照を固定するためのフックです。 このフックを使わないと、親コンポーネントがレンダリングされるたびに新しい関数が生成されてしまい、React memoによる再レンダリング抑止が効かなくなる可能性があります。

useMemo は特定の計算結果をメモ化するために利用できます。 リストをソートする処理などが頻繁に発生する場合、useMemoで結果をキャッシュすれば、余計な処理を減らすことができるでしょう。

これらの機能とReact memoを組み合わせることで、さらに効率的なレンダリングを実現できます。 ただ、あまりに多用するとコードが見づらくなるので、必要な部分だけに絞ると管理しやすいですね。

propsに渡す関数やオブジェクトはuseCallbackやuseMemoで最適化しておくと、React memoの効果が出やすくなります。

テストコードとアプリの拡張性を高めるヒント

React memoを導入したら、アプリ全体のテスト戦略にも気を配ると安心ですね。 たとえば、JestやReact Testing Libraryを使ってコンポーネントが想定どおりにレンダリングをスキップするか確認するテストを書くこともできます。

「このコンポーネントはpropsが変わらない限り、レンダリングされない」ということをテストで担保しておけば、将来的に変更を加えた際にもパフォーマンスが保たれるか判断しやすいでしょう。 また、コンポーネントが複雑になりすぎないよう、小さな単位に分割して適用するのがコツです。

大規模アプリケーションでは、いくつかのコンポーネントをReact memoでラップし、責任範囲を明確にすることで保守性が上がります。 このように、テストコードや設計とセットでReact memoを導入すれば、拡張性とパフォーマンスの両立を目指せるのではないでしょうか。

まとめ

React memoは、不要な再レンダリングを抑えてパフォーマンスを改善する仕組みとして有用です。 実務では大量データを扱うテーブルや、頻度の高い更新が行われるUIなどでとくに効果が出やすいでしょう。

ただし、propsが参照型で毎回変わるような場面では効果が限定的になります。 useCallbackやuseMemoを併用しながら、コンポーネント単位で最適化ポイントを探るのが実務的です。

また、カスタム比較関数を使うかどうかは、アプリの複雑度やパフォーマンス要件に左右されます。 慎重に設計すれば、React memoの利点を活かしつつ、コードの可読性も保ちやすいでしょう。

React 18でも基本の概念は同じですので、今後の機能追加を見据えてもReact memoの理解を深めておくと安心ですね。 皆さんも、もし「再レンダリングが多くて動きが重いかも」と感じる部分があるなら、React memoを検討してみてはいかがでしょうか。

Reactをマスターしよう

この記事で学んだReactの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。