React useMemoで処理を最適化する方法とは?初心者にもわかりやすく解説
はじめに
Reactで画面を作っていると、コンポーネントの再レンダリングが多く発生し、処理が重く感じる場面はないでしょうか。 皆さんが操作するUIや表示内容が複雑になるほど、不要な処理の繰り返しが増えてしまう場合があります。 こうした無駄を減らすために活躍するのが、useMemo というフックです。
useMemoは、コンポーネントが再レンダリングされるたびに行われる重い処理を最適化するために提供されています。 計算結果をメモ化しておくことで、依存関係に変化がない限りは同じ結果を再利用できます。 これを利用すると、ユーザー体感においてもスムーズな操作を実現しやすくなります。
今回は、プログラミング初心者の方に向けて、実務でよくあるシーンを踏まえながらuseMemoの基本やメリット、注意点を解説します。 React 18以降でも問題なく使える情報を中心にまとめていますので、最新環境でも安心して利用できるでしょう。
Reactの基本的なフックのおさらい
Reactには状態管理や副作用の処理を行うためのフックが複数用意されています。 皆さんも最初に学ぶことが多いのは、useStateやuseEffectではないでしょうか。 useStateはコンポーネント内で状態を管理するためのフックで、useEffectはデータ取得やDOM操作などの副作用を扱います。
こうしたフックを活用することで、クラスコンポーネントを使わなくても柔軟な設計が可能になります。 しかし、状態が複雑になったり、データ量が大きくなってくると、どうしても再レンダリングが増える場面が出てくるものです。
そこで注目されるのがuseMemoです。 余分な再レンダリングが行われることで、毎回行われる重たい計算処理やオブジェクト生成を最適化する手段としてよく利用されます。
useMemoとは
useMemo は、メモ化(caching)によって不要な再計算を避けるためのフックです。 何かしらの関数処理を行い、その結果を変数として利用する場面は多々あります。 特に配列のソートやフィルタリング、複雑なオブジェクトの生成など、時間がかかる処理であればあるほど再利用性が重要になります。
useMemoは以下のように呼び出します。 第1引数に計算処理のコールバック関数、第2引数にその計算結果を再生成するタイミングを決める依存配列を渡します。 依存配列の内容が変更されない限り、useMemoは前回の計算結果を使い回します。
import React, { useMemo } from "react"; function ExampleComponent({ items }) { const expensiveCalculation = useMemo(() => { // 重い処理や複雑な演算を行う return items.filter(item => item.value > 10).sort((a, b) => a.value - b.value); }, [items]); return ( <div> {expensiveCalculation.map(item => ( <p key={item.id}>{item.value}</p> ))} </div> ); } export default ExampleComponent;
このコードでは、items
に変化がなければ filter
と sort
の計算を再度行いません。
その結果、再レンダリングのたびに行われる複雑な処理を節約できます。
特徴と用途
useMemoの最大の特徴は、同じ依存関係なら同じ計算結果を再利用する点です。 Reactの再レンダリングは、親コンポーネントが更新されるたびに子コンポーネントも再描画の判定に入る仕組みです。 その際に行われる重い計算を抑制できるのがuseMemoの利点です。
一方で、まったくコストの低い処理に対してuseMemoを使っても、大きなパフォーマンス向上は見込めません。 むしろuseMemo自体のオーバーヘッドが増える可能性があります。 何でもかんでもメモ化すれば良いというものではないので注意が必要です。
使い方の基本
useMemoの使い方をシンプルに表すと、次のステップになります。
- メモ化したい処理をコールバック関数として定義する
- 処理の再実行を判断する依存配列を指定する
- 返り値にメモ化した結果を受け取り、コンポーネント内で使用する
この基本形を押さえておくと、状況に応じて活用しやすくなるはずです。 Reactでは、内部的に依存配列の値を監視し、変更がなければ前回計算した結果を返す仕組みを備えています。
具体的なコード例
実際にリストのフィルタリングやソートを行う例を見てみましょう。 以下では、数値のリストをソートし、その結果を表示しています。 ユーザーが入力した数値を追加するたびに再レンダリングは発生しますが、ソートロジックは値が変わらない場合に再実行されません。
import React, { useState, useMemo } from "react"; function NumberList() { const [numbers, setNumbers] = useState([5, 2, 12, 9]); const [inputValue, setInputValue] = useState(""); const sortedNumbers = useMemo(() => { // ソート処理(重いと仮定) return [...numbers].sort((a, b) => a - b); }, [numbers]); const handleAddNumber = () => { const newNumber = parseInt(inputValue, 10); if (!isNaN(newNumber)) { setNumbers([...numbers, newNumber]); } setInputValue(""); }; return ( <div> <div> <label> 数値を入力: <input type="number" value={inputValue} onChange={e => setInputValue(e.target.value)} /> </label> <button onClick={handleAddNumber}>追加</button> </div> <div> <p>オリジナル配列: {numbers.join(", ")}</p> <p>ソート済み配列: {sortedNumbers.join(", ")}</p> </div> </div> ); } export default NumberList;
ここでは、sortedNumbers
がuseMemoによって管理されます。
配列 numbers
に変更があったときだけ、配列をソートする処理を実行する仕組みです。
再レンダリングの仕組み
コンポーネントが再レンダリングされる条件として、ステートやプロパティが更新されると自動的に再描画が走ります。 しかし、再レンダリング時に必ずしも全ての処理を再度行う必要はありません。 useMemoを使うことで「同じ依存関係のもとであれば計算結果を再利用する」ことが可能になり、パフォーマンスを整えやすくなります。
これによって、無駄な処理が減り、複雑な画面でも比較的スムーズに動作しやすくなります。
useMemoが必要になるケース
React開発に慣れてくると、「この処理、毎回再計算しなくてもいいのにな」と感じることが出てきます。 そこがuseMemoの出番です。 以下のようなケースが考えられます。
- 大きな配列やオブジェクトを扱っており、フィルタリングやソートの処理が複雑
- 親コンポーネントの更新頻度が高く、子コンポーネントの再レンダリングをなるべく最小限に抑えたい
- コールバック関数内で外部リソースへのアクセスや大きな計算を行っている
こうした場合にuseMemoを導入すると、フレームワークのパフォーマンスを上手に引き出しやすくなります。
メモ化の仕組み
メモ化とは、過去に計算した結果を覚えておき、次回以降に同じ処理を行わないようにすることを指します。 useMemoは内部的に、依存配列の値を比較して変更があれば再度コールバック関数を実行し、変更がなければ以前の計算結果をそのまま返します。
このメモ化の仕組みが、Reactコンポーネントの無駄な計算を削減してくれます。 ただし依存配列が誤って設定されている場合は、期待通りに再計算されず、古い値を表示し続けるリスクもあるでしょう。
注意点
useMemoを使えば何でも解決というわけではありません。 初心者の方は、特に以下のポイントを押さえておくと良いでしょう。
本当に重い処理なのか
実行コストが軽い処理をuseMemoでメモ化しても、メリットが薄い可能性があります。
依存配列を正しく設定しているか
依存関係を細かく把握しないと、期待するタイミングで再計算が行われないことがあります。
過度なメモ化は可読性を損ねることがある
メモ化を増やしすぎるとコードの見通しが悪くなり、保守性が低下する場合があります。
useMemoによるメモ化はパフォーマンスを良くする便利な仕組みですが、適切なタイミングと目的が大切です。
実務でよくある活用シーン
実務の中では、APIから取得した大きなリストをフィルタリングして表示する処理などでuseMemoが使われることがあります。 たとえば、ECサイトのように数百件、数千件の商品リストを表示するときにフィルタ機能をつけるケースです。 「価格帯」「在庫状況」「ブランド」など、多彩な条件で絞り込みを行うと、ソート処理が頻繁に発生するかもしれません。
また、レイアウト計算やグラフ描画など、処理の重さが目に見えて分かるものにもuseMemoは効果的です。 複数のフィルタをかけ合わせる状況では、何度も再描画するよりも、依存関係が変わらなければ同じ結果を使いまわすほうが効率的でしょう。
useCallbackとの違い
同じようにReactでメモ化するフックとして useCallback も存在します。 useMemoが「値をメモ化」するのに対し、useCallbackは「関数をメモ化」します。 これはコンポーネントに関数を渡すとき、Propsの変更判定などで再定義を抑止したいときに使われます。
一見すると似ているふたつですが、メモ化の対象が異なるので使いどころも変わってきます。 useMemoは結果のメモ化、useCallbackは関数の再定義防止というイメージで使い分けると良いでしょう。
まとめ
今回はReactのuseMemo について、初心者にもわかりやすい形で説明しました。 再レンダリング時に不要な重い処理を実行しないようにすることで、アプリケーションの動作を軽くしやすくなります。 実務でも大きな配列のフィルタやソート処理など、さまざまなシーンで役立ちます。
ただし、すべてにuseMemoを使えば良いというわけではありません。 本当に処理が重いかどうか、あるいは依存配列が正しく設定されているかなど、いくつかのポイントを考慮して導入することが重要です。
メモ化の概念をしっかり押さえておけば、今後のReact開発で「余分な再計算が原因でアプリが重くなる」状況を回避できます。 ぜひ皆さんも、必要に応じてuseMemoを取り入れながら、快適なReact開発を目指してみてください。
useMemoはあくまで最適化の一手段です。 実際のアプリケーションでは、ボトルネックを把握しつつ、適所で活用するのがおすすめです。