見出し画像

メモ化全振りのReact開発:そのメリット・デメリットと選択理由

はじめに

こんにちは。株式会社SHIFT DAAE(ダーエ)部の野村です。

Reactの開発において、どのくらいメモ化を活用していますか? メモ化を全体的に用いる方もいれば、必要な部分だけを対象にする方もいます。今回は、それぞれの利点や欠点を考えた上で、私がどういうメモ化の活用戦略を選んだのかをお伝えします。

執筆者プロフィール:野村
SHIFT DAAE部所属の21卒のソフトウェアエンジニアです。 まだまだ勉強中です。

前提知識

こちらの項目はメモ化, React.memo, useMemo, useCallbackの簡単な説明となります。既知の方は読み飛ばしてください。

メモ化

Reactでは、"メモ化"とは主に関数の計算結果を記憶しておく(キャッシュ化する)テクニックを指します。これにより、同じ入力で何度も計算が行われることを避けることができ、レンダリングのパフォーマンスが向上します。 Reactの useMemo と useCallback は、いずれもメモ化(計算結果のキャッシュ化)の一部として使われます。

React.memo

React.memoは、関数コンポーネントのレンダリング結果をメモ化します。Props の値をチェックして再レンダリングの判断をしており、同じpropsで再度レンダリングが行われるとき、前回のレンダリング結果を再利用できます。

useMemo

useMemo は、重い計算結果をメモ化するためのフックです。このフックを使用すると、依存関係配列内の値が変化するまで、関数の結果はメモ化されます。これにより、値が変わらない限り、再計算する必要がなくなり、パフォーマンスが向上します。

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

上記のコードでは、computeExpensiveValue は a または b が変わったときのみ再計算され、その結果は memoizedValue に格納されます。

useCallback

useCallback は、特定の関数をメモ化するためのフックです。これは、依存関係配列にリストされた値が変化するまで、関数のインスタンスがメモ化されます。これにより、特に関数をプロパティとして子コンポーネントに渡す場合に、子コンポーネントが不必要に再レンダリングされるのを防げます。

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

上記のコードでは、doSomething は a または b が変わったときにのみ新しい関数インスタンスが作成されます。

これらのメモ化のテクニックは、計算コストが高い操作や、再レンダリングを避けたいときに有用です。

メモ化のメリット

  • 計算の再レンダリングを防ぎ、パフォーマンスを向上させる

  • useCallback は、特にカスタムフックでは責務の分離とカプセル化の観点から恩恵が得られる

詳しくは、こちらの記事をご覧ください。

メモ化のデメリット・懸念事項

  • アプリケーションがそもそもメモ化を必要としない可能性がある

  • 値が頻繁に変わるコンポーネントでは、メモ化は意味がなく、むしろオーバーヘッドが生じる

  • コードの可読性が低下する可能性がある

オーバーヘッドについては、ほぼ無視しても良いレベルであることがこちらの記事 で述べられています。

プロジェクトとして全てメモ化を選んだ理由

今回、私の取り組んでいるプロジェクトでは、「useMemoを使用できないケースを除き、useMemo、useCallbackは基本的に全てに適用する」という方針を採用しました。Reactの公式ドキュメントにもメモ化について触れています。

これらのドキュメントを見ると、ベストプラクティスとしては「必要な箇所のみにメモ化する」ことが推奨されています。しかし、我々のプロジェクトでは以下の理由から全てメモ化の方針を採用しました。

  • 再描画の必要がないコンポーネントを意識的に制御する設計方針を採用したかった

  • デメリットや懸念事項がそれほど大きな問題とは思われなかった

  • 新しく入ったメンバーによるコードの質の差異を避けたかった

  • コーディングスタイルを統一するため

  • useMemo、useCallbackを使用するかどうかの判断コストを削減するため

公式ドキュメントでも、全てを可能な限りメモ化するアプローチについて触れています。各ケースを考慮しなくてもよくなるという利点があると解釈しました。

useMemoを使用できないケース

  • フックをループや条件分岐、あるいはネストされた関数内で呼び出せない(詳細)

  • useMemoの使用により挙動が変更される場合

メモ化はパフォーマンス最適化の手法であり、レンダリングの抑止や挙動の変更を目的としたものではないことに注意が必要です。 参考: Reactの最上位API-React フック API リファレンス - React Reactのメモ化と、メモ化できないケースについて

実践から得た感想

公式ドキュメントでは可読性の低下が懸念されていますが、私自身はそれほど気になりませんでした。また、再レンダリングロジックが高コストな実装を必要とする場合でも、メモ化する方針を既に決めているため、誤った判断をすることがないと感じました。これは大きなメリットだと思います。

終わりに

私がメモ化を全てに適用する方針を採用した理由とその結果を共有しました。全てメモ化のアプローチが全てのプロジェクトに適しているわけではありませんが、その適用にあたっての考慮点として、この記事が役立つと嬉しいです。ありがとうございました。

\もっと身近にもっとリアルに!DAAE公式Twitter/

PHOTO:UnsplashAbdul A

お問合せはお気軽に
https://service.shiftinc.jp/contact/

SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/

SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/

SHIFTの導入事例
https://service.shiftinc.jp/case/

お役立ち資料はこちら
https://service.shiftinc.jp/resources/

SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/