React NativeのuseMemoフックは、コンポーネントのレンダリング間で計算結果をメモ化し、高コストな操作の繰り返し実行を防ぎます。関数をメモ化するuseCallbackとは異なり、useMemoは返された値を保持し、指定された依存関係が変更された場合にのみ再計算します。React Docs、2024によると、メモ化は大規模な配列や複雑なデータ変換を伴う計算に特に効果的です。React Nativeでは、不要な再レンダリングが発生するたびにJavaScriptとネイティブスレッド間のブリッジ接続に追加の負荷がかかるため、useMemoを適切に使用することで、アニメーションの滑らかさとインターフェースの応答速度に直接影響を与えます。
重要ポイント
useMemo — はReact標準ライブラリのフックで、追加パッケージなしでReact Nativeで使用できます。渡された関数が返す値をメモ化し、配列で指定された依存関係が変更されるまで、後続のレンダリングでこの値を再利用します。
フックの名前はmemoization(メモ化)に由来します — 高コストな関数の結果をキャッシュに保存する最適化手法です。同じ引数での後続の呼び出しでは、再計算する代わりにキャッシュされた値が返されます。
React Nativeのコンテキストでは、アーキテクチャ上の特性からメモ化が特に重要です。コンポーネントが再レンダリングされるたびに、JavaScript — ネイティブブリッジを介してデータが送信され、時間がかかります。コンポーネントに高コストな計算(リストのフィルタリング、並べ替え、フォーマット)が含まれている場合、再レンダリングのたびにそれらが繰り返され、JSスレッドがブロックされ、フレームドロップが発生します。
React Native Performance Docs、2024によると、ボトルネックは多くの場合、ネイティブコンポーネントのレンダリングではなく、レンダリング間のJavaScriptロジックの実行です。useMemoはまさにこの問題を解決します。
useMemoは関数と依存関係配列を受け取り、メモ化された値を返します。Reactは前回の値と依存関係配列を保存します。各レンダリングで、ReactはObject.isを使用して現在の依存関係を前回のものと比較します。少なくとも1つの依存関係が変更された場合、関数が再実行され、結果が保存されます。
useMemoのシグネチャはウェブとReact Nativeで完全に同じです。最初の引数は、メモ化する値を返すパラメータなしの関数です。2番目は依存関係配列で、これが変更されると値が再計算されます。
import React, { useMemo } from 'react';
const sortedList = useMemo(() => {
return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
この例では、sortedListはitems配列への参照が変更された場合にのみ再計算されます。配列の内容は変更されたが参照が同じままの場合(ミューテーション)、useMemoは変更を検出しません — これは重要な制限です。
Reactは依存関係の比較にObject.isアルゴリズムを使用します。これは厳密な等価性===と同様に機能しますが、NaNを正しく処理します(+0と-0は異なるとみなされます)。つまり、プリミティブ型(文字列、数値、ブール値)の場合は値で比較され、オブジェクトと配列の場合は参照で比較されます。
// Object.is({'a': 1}, {'a': 1}) -> false (different refs)
// Object.is(42, 42) -> true (primitive by value)
// Object.is(NaN, NaN) -> true (correct handling of NaN)
このメカニズムを理解することは重要です: レンダリングのたびに依存関係に新しいオブジェクトを渡すと、依存関係が毎回変更されたとみなされるため、メモ化は無駄になります。
useMemoは3つのシナリオで有効です: 高コストな計算、子コンポーネントへの安定したpropsの受け渡し、オブジェクトの参照IDの保持。React Nativeでは、モバイルアプリケーションのアーキテクチャ上、これら3つのシナリオが定期的に発生します。
コンポーネントが大規模なデータ配列(フィルタリング、並べ替え、グループ化)やリソース集約型の数学的操作を処理する場合、useMemoは再レンダリングのたびにこれらの計算が繰り返されるのを防ぎます。モバイルアプリケーションでは、APIからのデータをリストに表示する前にフォーマットするのが典型的な例です。
子コンポーネントがReact.memoでラップされている場合、propsが変更されたときにのみ再レンダリングされます。親内部でuseMemoなしでpropオブジェクトが作成されると、親のレンダリングのたびに新しい参照が作成され、子コンポーネントが不必要に再レンダリングされ、React.memoの利点が無効になります。
React Nativeでは、スタイルはしばしばpropsに依存します — 例えば、要素の幅は画面サイズに基づいて計算されます。useMemoを使用すると、入力パラメータが変更された場合にのみ、このような動的スタイルを計算できます。
useMemoは万能な最適化ツールではありません。いくつかのシナリオではメリットがなく、依存関係の保存と比較のオーバーヘッドによりパフォーマンスが低下する可能性さえあります。メモリの1バイト1バイトが重要なReact Nativeでは、効果を測定せずにuseMemoを盲目的に適用するのはアンチパターンです。
フックがメリットをもたらさない主なケースは3つです。第一に、計算が単純な場合(単純な加算、文字列連結)、useMemoのコストが計算自体のコストを上回ります。第二に、依存関係がレンダリングのたびに変更される場合 — 関数はそれでも実行され、メモ化は単に余分な作業を追加するだけです。第三に、コンポーネントのレンダリング頻度が低い場合、キャッシュストレージのオーバーヘッドは正当化されません。
Kent C. Dodds、2023によると、useMemoを追加する前に、React DevTools ProfilerまたはReact Nativeに組み込まれたパフォーマンスモニターを使用して問題を測定する必要があります。コンポーネントの再レンダリングに1ミリ秒未満しかかからない場合、メモ化は不要です。
useMemoとuseCallback — よく混同されるReactの2つのメモ化フックです。違いは、それらが保持するものにあります: useMemoは関数の実行結果(任意の値)を返し、useCallbackは関数自体を返します。どちらも依存関係配列を受け取りますが、構文が異なります。
| 特性 | useMemo | useCallback |
|---|---|---|
| 返り値 | 関数実行の結果 | 関数への参照 |
| 最初の引数 | 値を返す関数 | メモ化する関数 |
| 典型的な使用法 | 計算のキャッシュ | 子コンポーネント用の安定したコールバック |
| 等価性 | useMemo(() => fn, deps) | useCallback(fn, deps) === useMemo(() => fn, deps) |
実際には、useCallbackは関数をメモ化する必要がある場合の、useMemoに対するシンタックスシュガーです。React Nativeでは、両方のフックが同様に頻繁に使用されます: useMemo — データ用(フォーマットされたテキスト、計算されたスタイル)、useCallback — イベントハンドラー用(onPress、onChangeText)。
React NativeアプリケーションでのuseMemoの2つの実際のシナリオを見てみましょう。1つ目は検索クエリによる連絡先リストのフィルタリング、2つ目は画面サイズに基づく動的スタイルの計算です。
この例では、useMemoは、検索クエリとソースデータの両方が変更されていない場合、大きな連絡先リストのレンダリングごとの繰り返しフィルタリングを防ぎます。
const ContactList = ({ contacts, searchQuery }) => {
const filteredContacts = useMemo(() => {
if (!searchQuery.trim()) return contacts;
const query = searchQuery.toLowerCase();
return contacts.filter((c) =>
c.name.toLowerCase().includes(query) ||
c.phone.includes(query)
);
}, [contacts, searchQuery]);
return (
<FlatList
data={filteredContacts}
renderItem={renderContact}
keyExtractor={(item) => item.id}
/>
);
};
useMemoがない場合、フィルタリングはすべてのレンダリングで実行され、アニメーションやコンポーネントの他の部分の状態変更によって引き起こされるものも含まれます。useMemoを使用すると、フィルタリングはcontactsまたはsearchQueryが変更された場合にのみ実行されます。
React Nativeでは、スタイルはしばしば画面サイズやコンポーネントのpropsに依存します。useMemoを使用すると、このようなスタイルオブジェクトを一度計算し、依存関係が変更されるまで再利用できます。
const Card = ({ title, isSelected }) => {
const cardStyle = useMemo(() => ({
backgroundColor: isSelected ? '#e3f2fd' : '#ffffff',
borderWidth: isSelected ? 2 : 1,
padding: 16,
}), [isSelected]);
return (
<View style={cardStyle}>
<Text>{title}</Text>
</View>
);
};
このアプローチにより、cardStyleオブジェクトは、isSelectedが変更されるまで安定した(同じ参照)状態を保ちます。CardがReact.memoでラップされている場合、このパターンは子要素の不要な再レンダリングを防ぎます。
よくある質問
useMemoはメモ化された値を返します — 最初の引数として渡された関数の実行結果です。後続のレンダリングでは、依存関係が変更されていない場合、Reactはキャッシュされた値を返します。
useMemoは関数実行の結果(任意の値: 数値、オブジェクト、JSX)を返し、useCallbackは関数自体を返します。useCallback(fn, deps)はuseMemo(() => fn, deps)と同等です。
useMemoは単純な計算(加算、連結)、まれな再レンダリング、レンダリングごとに変更される依存関係、およびプリミティブ型には無駄です。useMemoを追加する前に、常にパフォーマンスを測定してください。
はい、不必要に使用した場合。Reactは以前の値をメモリに保存し、レンダリングのたびに依存関係を比較します。単純な計算の場合、これらの操作のオーバーヘッドがメモ化のメリットを上回ります。
React DevTools Profilerを使用してコンポーネントのレンダリング時間を測定します。React Nativeでは、デベロッパーメニューからパフォーマンスモニターも利用できます。再レンダリングに1ミリ秒未満しかかからない場合、useMemoは不要です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。