😺

メモ化との付き合い方を考える📝

2024/10/08に公開

紹介すること

Reactで開発する上でmemo,useMemo,useCallbackとどう付き合っていくべきかについて考察します。
これらのメモ化技術をチーム単位でどのように取り入れていくかの方針を、具体に依存せず抽象的な観点から紹介します。

紹介しないこと

  • メモ化の各API、hooksの機能
  • 具体的な使い方、ユースケース

結論

具体的な基準を設けず、コンポーネントや関数、値ごとにメモ化が必要かどうかを、各エンジニアが判断して実施するのが良いと考えます。

まずはメモ化のメリットやデメリットを紹介し、それらを踏まえた上で上記の理由を紹介します。

メモ化のメリットとデメリット

メリット

  • 再レンダリングを抑えることができる
  • デバッグしやすい
  • エンジニアの技術力向上

デメリット

  • オーバーヘッド
  • 可読性の低下
  • 労力

順に解説します。

メリット

再レンダリングを抑えることができる

再レンダリングを抑えることでパフォーマンス向上が見込めます。

メモ化をすることで、得られるパフォーマンス向上はミリ秒単位となることが多く、クリティカルなパフォーマンスの成果/問題とはなりにくいです。

デバッグしやすい

再レンダリングを抑えられるので、ログが大量に出て追いにくくなるのを避けることができます。

エンジニアの技術力向上

必然的にレンダリングプロセスや、props設計、パフォーマンスを考えることになるので、エンジニアの技術力向上が期待できます。

以前の僕は、メモ化をしても数ミリ秒程度の差しか生じないのであれば、メモ化を全くしないか、すべてメモ化するほうが効率的だと考えていました。
メモ化によるパフォーマンスの向上よりも、実装スピードを優先したかったからです。

しかし、メモ化を意識するようになったことで、レンダリングやパフォーマンスについて深く考えるようになり、Reactをしっかり学ぶ良いきっかけとなりました。

デメリット

オーバーヘッド

メモ化によるオーバーヘッドが原因で、逆にパフォーマンスが悪化することもあります。
したがって、メモ化が本当に必要な箇所を見極めて適用する必要があります。

とはいえ、オーバーヘッドに関しては、クリティカルな問題になることは少ないため、積極的にチャレンジしましょう。

可読性の低下

コンポーネントや関数、値の前後で記述量は増えます。

以下は、メモ化しなかった場合とメモ化した場合の記述の例です。

メモ化しない場合
import React from 'react';

const MyComponent = ({ value, onClick }) => {
  console.log('Rendering MyComponent');

  return (
    <div>
      <p>{value}</p>
      <button onClick={onClick}>Click Me</button>
    </div>
  );
};

export const ParentComponent = () => {
  const [count, setCount] = React.useState(0);
  const [text, setText] = React.useState("Hello, World!");

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <MyComponent 
        value={text} 
        onClick={() => setText("Button Clicked!")} 
      />
    </div>
  );
};

メモ化した場合
import React, { useCallback, useMemo } from 'react';

const MyComponent = React.memo(({ value, onClick }) => {
  console.log('Rendering MyComponent (memoized)');

  return (
    <div>
      <p>{value}</p>
      <button onClick={onClick}>Click Me</button>
    </div>
  );
});

export const ParentComponent = () => {
  const [count, setCount] = React.useState(0);
  const [text, setText] = React.useState("Hello, World!");

  // useCallbackでonClick関数をメモ化
  const handleClick = useCallback(() => {
    setText("Button Clicked!");
  }, []);

  // useMemoで値をメモ化
  const memoizedValue = useMemo(() => text, [text]);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <MyComponent value={memoizedValue} onClick={handleClick} />
    </div>
  );
};

メモ化をすると記述量は多少増えるものの、可読性が著しく悪化するとは感じません。そのため、可読性に関しては過度に気にしなくて良いと思います。

労力

単純にメモ化のAPIやHooksでラップするだけでなく、依存関係の値や関連するコンポーネントを見極めてメモ化する必要があります。
これにより、メモ化しない実装に比べて実装スピードが遅くなることは確かです。

しかしながら、実装時が最も早く効率的にメモ化できるタイミングです。後回しにせず、実装時にメモ化しておいたほうが良いと思います。

メモ化のパフォーマンスがプロダクトに与える影響

ほぼないと言えると思います

もちろん、パフォーマンスは高ければ高いほど良いものです。しかし、可読性や労力など、他の要素とのバランスを考慮し、パフォーマンスの妥協点を見つける必要があります。

Google Chrome チームが提唱した「RAIL」というパフォーマンス指標があります。

https://developer.mozilla.org/ja/docs/Glossary/RAIL

メモ化によるパフォーマンス改善は数ミリ秒程度にとどまることが多いため、大きな効果を期待できる場面は限られます。
パフォーマンスチューニングが必要になった際は、メモ化以外の要因を改善する方が効果的である場合が多いはずです。

結論の理由

メモ化は、しすぎ/しなさすぎどちらでも大きな影響を与えることは少ないと思います。

そのため、各エンジニアに創意工夫の余地を残し、メモ化を任せることで、スキル向上に対する意欲を高めることができると考えます

エックスポイントワン技術ブログ

Discussion