React Native FlatListのkeyExtractor徹底解説:パフォーマンス最適化の鍵
FlatList
とは?
まず、FlatList
は、大量のデータを効率的に表示するためのReact Nativeのコンポーネントです。ScrollView
とは異なり、画面に表示されているアイテムのみをレンダリングし、スクロールに応じて不要なコンポーネントを再利用することで、メモリ使用量を最適化し、スムーズなスクロールを実現します。
なぜkeyExtractor
が必要なのか?
React(およびReact Native)がリストをレンダリングする際、各アイテムに一意の「キー」を持たせることを強く推奨しています。このキーは、Reactがリスト内のアイテムが追加、削除、または順序変更されたときに、どのアイテムが変更されたかを効率的に識別するために使用されます。
キーがない場合や一意でない場合、Reactはリストの変更を正しく追跡できず、不要な再レンダリングが発生したり、UIの挙動が不安定になったりする可能性があります。例えば、リスト内のアイテムの順序が入れ替わったときに、キーがないとReactはアイテムが入れ替わったことを認識できず、リスト全体を再レンダリングしてしまうことがあります。これはパフォーマンスの低下につながります。
keyExtractor
の役割
keyExtractor
は、FlatList
に渡すデータ配列の各アイテムから、一意のキーを抽出する方法を指示する関数です。
デフォルトでは、FlatList
はデータアイテムのkey
プロパティ、次にid
プロパティをキーとして使用しようとします。しかし、もしあなたのデータにkey
やid
という名前のプロパティがない場合や、異なる名前(例: _id
、itemId
など)で一意の識別子を持っている場合に、keyExtractor
を使って明示的にキーを抽出するロジックを提供する必要があります。
keyExtractorの書式
keyExtractor
は、item
(現在のアイテム)とindex
(アイテムのインデックス)の2つの引数を受け取り、一意の文字列を返す関数です。
<FlatList
data={yourDataArray}
renderItem={({ item }) => <Text>{item.name}</Text>}
keyExtractor={(item, index) => item.uniqueId.toString()} // ここがkeyExtractor
/>
例
もしあなたのデータが以下のような形式だとします。
const myData = [
{ productId: 'a1', name: 'りんご' },
{ productId: 'b2', name: 'みかん' },
{ productId: 'c3', name: 'バナナ' },
];
この場合、productId
が一意の識別子なので、keyExtractor
は次のようになります。
<FlatList
data={myData}
renderItem={({ item }) => <Text>{item.name}</Text>}
keyExtractor={(item) => item.productId} // item.productIdをキーとして使用
/>
もしデータがプリミティブ型(文字列や数値の配列)の場合、インデックスをキーとして使用することもできます。ただし、リストのアイテムの順序が変更される可能性がある場合は、インデックスをキーとして使用することは推奨されません。
const names = ['太郎', '花子', '一郎'];
<FlatList
data={names}
renderItem={({ item }) => <Text>{item}</Text>}
keyExtractor={(item, index) => String(index)} // インデックスをキーとして使用
/>
keyExtractor
を適切に設定しないと、React Nativeは「VirtualizedList: missing keys」のような警告を表示することがあります。これにより、リストが正しく追跡されず、スクロール時のパフォーマンスの問題や、アイテムの再レンダリングに関する予期せぬ挙動が発生する可能性があります。
- 重要性
- Reactがリストの変更(追加、削除、順序変更)を効率的に追跡し、必要な部分のみを再レンダリングするのに役立つ。
- これにより、リストのスクロールパフォーマンスが向上し、UIのちらつきや予期せぬ挙動を防ぐことができる。
- 一意で安定したキーを使用することが推奨される(データ自体に一意なIDがある場合はそれを使用)。
- 機能
FlatList
に渡されるデータ配列の各アイテムから、一意のキー(文字列)を抽出する関数を定義する。 - 目的
FlatList
が各リストアイテムを一意に識別できるようにし、パフォーマンスと安定性を向上させる。
「VirtualizedList: missing keys for items...」または「Each child in a list should have a unique "key" prop」警告
これはFlatList
を使用する上で最も頻繁に見られる警告です。Reactがリスト内のアイテムを一意に識別するためのキーを見つけられない場合に表示されます。
原因
- renderItem内で別途keyプロップを設定している(誤った使い方)
FlatList
はkeyExtractor
を通じてキーを管理するため、renderItem
内で個々のコンポーネントにkey
プロップを直接設定する必要はありません。むしろ、設定すると混乱を招くことがあります。 - キーがユニークでない
複数のアイテムが同じキーを持っている場合。 - 返されるキーが文字列ではない
keyExtractor
は文字列を返す必要がありますが、数値などをそのまま返している場合。 - keyExtractorが設定されていないか、正しく設定されていない
FlatList
はデフォルトでデータアイテムのkey
プロパティ、次にid
プロパティをキーとして使用しようとしますが、これらのプロパティがデータに存在しない場合。keyExtractor
を定義しているものの、その関数が実際には一意の値を返していない場合。
トラブルシューティング
- keyExtractorが定義されているか確認する
<FlatList data={myData} renderItem={({ item }) => <MyListItem item={item} />} keyExtractor={(item, index) => item.id.toString()} // ここを必ず確認 />
- キーとして使用するプロパティがデータ内で一意であることを確認する
ほとんどの場合、サーバーから取得したデータにはid
や_id
のような一意の識別子が含まれています。それらを活用しましょう。const myData = [ { id: 'uuid-1', name: 'アイテムA' }, { id: 'uuid-2', name: 'アイテムB' }, // ... ]; keyExtractor={(item) => item.id} // item.id が一意であると仮定
- キーが文字列として返されていることを確認する
数値型のIDを使用している場合は、toString()
を使って文字列に変換します。keyExtractor={(item) => item.id.toString()} // 数値IDの場合
- インデックスをキーとして使用する際の注意
- データが静的で、リストの並び順が変更されたり、アイテムが追加・削除されたりすることがない場合にのみ、インデックスをキーとして使用できます。
- 動的なリストでインデックスをキーとして使用すると、パフォーマンスの問題や、アイテムの表示が崩れるなどの予期せぬ挙動を引き起こす可能性があります。
- どうしてもインデックスを使う場合は、文字列に変換します。
keyExtractor={(item, index) => index.toString()}
リストのスクロールパフォーマンスが悪い / UIのちらつき
原因
- renderItem関数がコンポーネント内で定義されている
renderItem
関数がコンポーネントのレンダリングごとに再作成されると、FlatList
の最適化が効かなくなり、パフォーマンスが低下します。 - キーが一意でない、または安定していない
アイテムが追加・削除・並び替えられた際に、Reactが変更を正しく追跡できず、不要な再レンダリングが発生するため。特に、Math.random()
などのように毎回異なる値を生成する関数をキーとして使用するのは絶対に避けるべきです。
トラブルシューティング
- 一意で安定したキーを使用する
データを識別する最も信頼性の高い、変更されない値(例:DBのID)を使用します。 - renderItem関数をコンポーネントの外に定義するか、useCallbackでメモ化する
または// コンポーネントの外に定義 const renderListItem = ({ item }) => <MyListItem item={item} />; function MyList() { // ... return ( <FlatList data={myData} renderItem={renderListItem} // 外で定義した関数を使用 keyExtractor={(item) => item.id.toString()} /> ); }
useCallback
を使用(関数コンポーネントの場合):import React, { useCallback } from 'react'; function MyList() { // ... const renderListItem = useCallback(({ item }) => { return <MyListItem item={item} />; }, []); // 依存配列が空なら、この関数は一度だけ作成される return ( <FlatList data={myData} renderItem={renderListItem} keyExtractor={(item) => item.id.toString()} /> ); }
- getItemLayoutプロップを使用する (アイテムの高さが一定の場合)
これを設定すると、FlatList
が各アイテムのレイアウトを動的に計算する必要がなくなり、大幅なパフォーマンス改善が見込めます。<FlatList data={myData} renderItem={renderListItem} keyExtractor={(item) => item.id.toString()} getItemLayout={(data, index) => ( { length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index } )} />
ITEM_HEIGHT
は各アイテムの固定の高さです。 - removeClippedSubviewsプロップをtrueにする
FlatList
が画面外のビューをアンマウントしてメモリを節約します。ただし、これによってまれに表示上の問題が発生することもあるので、注意が必要です。
データソースの変更がUIに反映されない
データは更新されているはずなのに、FlatList
の表示が古いままになることがあります。
原因
- keyExtractorが安定したキーを返していない
前述のパフォーマンス問題と同様に、キーが頻繁に変わることで、Reactがアイテムの変更を正しく追跡できないため。 - dataプロップが正しく更新されていない
Reactのコンポーネントは、propsやstateが変更された場合にのみ再レンダリングされます。data
配列自体が参照として変更されていない場合(配列内の要素が変更されただけで、配列の参照は同じままの場合)、FlatList
は再レンダリングの必要性を認識しないことがあります。
トラブルシューティング
- 新しい配列参照を渡すことでdataプロップを更新する
配列の内容を変更するだけでなく、新しい配列を作成してFlatList
に渡すようにします。// 例: アイテムを削除する場合 const updatedData = myData.filter(item => item.id !== deletedItemId); setMyData(updatedData); // stateを更新して新しい配列を渡す
- keyExtractorが安定したキーを返していることを再確認する
これが最も重要です。
keyExtractorの引数itemがundefinedになる
keyExtractor
関数内でitem
プロパティにアクセスしようとしたときにエラーが発生する場合。
原因
- データが非同期で取得され、まだ利用可能になっていない
初回レンダリング時にdata
がnull
やundefined
であるにもかかわらず、keyExtractor
が実行されてしまう。 - dataプロップに渡されたデータが期待される形式ではない
data
配列の各要素がオブジェクトではなく、プリミティブ値(文字列や数値)である場合や、データが空である場合。
トラブルシューティング
- dataプロップに正しいデータが渡されているか確認する
// dataが[ { id: 1, name: 'A' }, { id: 2, name: 'B' } ] のような形式であることを確認 console.log(myData); // データの内容を確認
- データのロード状態を考慮する
データが非同期でロードされる場合は、データが利用可能になるまでFlatList
をレンダリングしないようにするか、data
プロップに空の配列を渡します。
または、{myData && myData.length > 0 ? ( <FlatList data={myData} renderItem={({ item }) => <MyListItem item={item} />} keyExtractor={(item) => item.id.toString()} /> ) : ( <Text>データがありません</Text> )}
data={myData || []}
のようにデフォルト値を設定することもできます。 - keyExtractor内でnull/undefinedチェックを行う
ただし、後者のkeyExtractor={(item, index) => (item && item.id ? item.id.toString() : index.toString())}
index.toString()
は一時的な回避策であり、根本的なデータ設計を見直す方が良いでしょう。
リストアイテム内の特定のコンポーネントのプロパティが変更されても、UIが更新されない場合。
原因
- keyExtractorの不適切さ
キーが安定していないと、Reactがアイテムを再利用すべきか、新しく作成すべきかを判断しにくくなり、更新の不具合につながることがあります。 - FlatListがPureComponentであることによる最適化の副作用
FlatList
はPureComponent
として実装されており、propsが浅い比較(shallow comparison)で変更がなければ再レンダリングされません。renderItem
の内部で参照型のプロパティ(オブジェクトや配列)が変更されても、その参照自体が変わっていなければ、FlatList
は変更を検知しません。
- extraDataプロップを使用する
renderItem
が依存する親コンポーネントのstateやpropsがあり、それらが更新されたときにFlatList
全体を再レンダリングさせたい場合にextraData
プロップにそれらの値を渡します。const [selectedId, setSelectedId] = useState(null); return ( <FlatList data={myData} renderItem={({ item }) => ( <MyListItem item={item} isSelected={item.id === selectedId} onPress={() => setSelectedId(item.id)} /> )} keyExtractor={(item) => item.id.toString()} extraData={selectedId} // selectedIdが変更されたらFlatListも再レンダリング /> );
- renderItem内のコンポーネントをReact.memoでメモ化する
MyListItem
コンポーネント自体が不要な再レンダリングを避けるように、React.memo
でラップすることを検討します。ただし、この場合もkeyExtractor
は適切である必要があります。const MyListItem = React.memo(({ item, isSelected, onPress }) => { // ... });
FlatList
の基本構造
まず、FlatList
の基本的な構造を見てみましょう。
import React from 'react';
import { FlatList, Text, View, StyleSheet } from 'react-native';
const DATA = [
{ id: '1', title: '最初のアイテム' },
{ id: '2', title: '次のアイテム' },
{ id: '3', title: 'もっと次のアイテム' },
// ...さらに多くのアイテム
];
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const App = () => {
return (
<FlatList
data={DATA} // 表示するデータ配列
renderItem={({ item }) => <Item title={item.title} />} // 各アイテムのレンダリング方法
// keyExtractor={...} // ここにkeyExtractorを設定します
/>
);
};
const styles = StyleSheet.create({
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 32,
},
});
export default App;
この基本構造に対して、keyExtractor
をどのように設定するかを見ていきます。
例1:id
プロパティをキーとして使用する (推奨)
最も一般的なケースで、データ配列の各オブジェクトが一意のid
プロパティを持っている場合です。
import React from 'react';
import { FlatList, Text, View, StyleSheet } from 'react-native';
const DATA = [
{ id: 'item-1', title: 'リンゴ' },
{ id: 'item-2', title: 'ミカン' },
{ id: 'item-3', title: 'バナナ' },
{ id: 'item-4', title: 'イチゴ' },
{ id: 'item-5', title: 'ブドウ' },
];
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const App = () => {
return (
<FlatList
data={DATA}
renderItem={({ item }) => <Item title={item.title} />}
keyExtractor={(item) => item.id} // 各アイテムの 'id' プロパティをキーとして使用
/>
);
};
const styles = StyleSheet.create({
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 32,
},
});
export default App;
解説
FlatList
は、このid
を使って各アイテムのDOM要素にキーを設定し、リストの変更を効率的に追跡します。keyExtractor={(item) => item.id}
: この行がkeyExtractor
の設定です。item
:FlatList
のdata
配列から取り出された現在のアイテムオブジェクトを指します。item.id
: 現在のアイテムのid
プロパティの値を返します。このid
は、データ内で一意である必要があります。
例2:_id
など、異なるプロパティ名をキーとして使用する
データソースによっては、一意の識別子がid
以外の名前(例: MongoDBの_id
や、uuid
、productId
など)で提供されることがあります。
import React from 'react';
import { FlatList, Text, View, StyleSheet } from 'react-native';
const PRODUCTS = [
{ _id: 'prod-001', name: 'MacBook Air M3' },
{ _id: 'prod-002', name: 'iPad Pro M4' },
{ _id: 'prod-003', name: 'iPhone 15 Pro' },
];
const ProductItem = ({ name }) => (
<View style={styles.item}>
<Text style={styles.title}>{name}</Text>
</View>
);
const App = () => {
return (
<FlatList
data={PRODUCTS}
renderItem={({ item }) => <ProductItem name={item.name} />}
keyExtractor={(item) => item._id} // '_id' プロパティをキーとして使用
/>
);
};
const styles = StyleSheet.create({
item: {
backgroundColor: '#a2d2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 24,
color: '#333',
},
});
export default App;
解説
- 重要なのは、そのプロパティの値が一意であることです。プロパティ名自体は何でも構いません。
keyExtractor={(item) => item._id}
: この場合、item._id
が各プロダクトを一意に識別するキーとして使用されます。
例3:IDが数値の場合、文字列に変換してキーとして使用する
keyExtractor
は文字列のキーを期待します。もしIDが数値型の場合、toString()
を使って文字列に変換する必要があります。
import React from 'react';
import { FlatList, Text, View, StyleSheet } from 'react-native';
const USERS = [
{ userId: 101, username: 'Alice' },
{ userId: 102, username: 'Bob' },
{ userId: 103, username: 'Charlie' },
];
const UserItem = ({ username }) => (
<View style={styles.item}>
<Text style={styles.title}>{username}</Text>
</View>
);
const App = () => {
return (
<FlatList
data={USERS}
renderItem={({ item }) => <UserItem username={item.username} />}
keyExtractor={(item) => item.userId.toString()} // 数値IDを文字列に変換
/>
);
};
const styles = StyleSheet.create({
item: {
backgroundColor: '#bde0fe',
padding: 15,
marginVertical: 6,
marginHorizontal: 12,
},
title: {
fontSize: 20,
color: '#0056b3',
},
});
export default App;
解説
keyExtractor={(item) => item.userId.toString()}
:userId
が数値なので、toString()
を使って文字列に変換しています。これを忘れると、「VirtualizedList: missing keys...」のような警告が出る可能性があります。
データ配列のオブジェクトに一意のIDプロパティがない場合、そのアイテムのインデックスをキーとして使用することができます。しかし、これは推奨されません。
- なぜ推奨されないのか?:
- リストのアイテムが追加、削除、または並び替えられた場合、インデックスは変化します。
- Reactは、キーが変化するとそのアイテムが「別物」になったと判断し、不要な再レンダリングを引き起こしたり、状態がリセットされたり、UIが予期せぬ挙動を示す可能性があります。
- 例えば、リスト内のアイテムの順序を入れ替えただけで、すべてのアイテムが再レンダリングされることがあります。
使用が許容される非常に限定的なケース
- リストが静的で、今後一切、アイテムの追加・削除・並び替えが行われないことが保証されている場合。
import React from 'react';
import { FlatList, Text, View, StyleSheet } from 'react-native';
const COLORS = ['Red', 'Green', 'Blue', 'Yellow', 'Purple']; // プリミティブ型の配列
const ColorItem = ({ colorName }) => (
<View style={[styles.item, { backgroundColor: colorName.toLowerCase() }]}>
<Text style={styles.title}>{colorName}</Text>
</View>
);
const App = () => {
return (
<FlatList
data={COLORS}
renderItem={({ item, index }) => <ColorItem colorName={item} />}
keyExtractor={(item, index) => index.toString()} // インデックスをキーとして使用(非推奨)
/>
);
};
const styles = StyleSheet.create({
item: {
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
borderRadius: 8,
},
title: {
fontSize: 28,
color: 'white',
fontWeight: 'bold',
textAlign: 'center',
},
});
export default App;
解説
- この例のように、データがプリミティブな文字列の配列で、かつ順序が変わらないことが確実な場合にのみ、インデックスの使用を検討できます。しかし、可能な限り一意のIDをデータに持たせるように設計するべきです。
keyExtractor={(item, index) => index.toString()}
:keyExtractor
は第2引数としてindex
も受け取ります。ここではそのindex
を文字列に変換してキーとしています。
keyExtractor
の主な目的は、FlatList
のパフォーマンスと安定性を高めることです。
- 避けるべき方法
Math.random()
のように毎回異なる値を生成する関数をキーとして使用すること。インデックスをキーとして使用することも、リストが動的である場合は避けるべきです。 - 注意点
keyExtractor
は文字列を返す必要があります。数値IDの場合はtoString()
で変換する。 - 最も推奨される方法
データソースの各アイテムに存在する一意で安定したIDプロパティ(例:id
,_id
,uuid
など)をキーとして使用する。
React NativeのFlatList
において、keyExtractor
はリストの要素を一意に識別するために必須のプロパティです。これは、Reactのリストレンダリングの仕組みにおいて、要素の追加、削除、並び替えを効率的に行うための基盤となります。
そのため、「keyExtractor
の代替方法」という表現は、厳密には「keyExtractor
を使わない方法」ではなく、「keyExtractor
の内部で、どのようなロジックを使ってキーを生成するか」のバリエーションを指すことが多いです。
keyExtractor
の代替ロジック(keyExtractor
自体は使用する)
これは、keyExtractor
プロパティ自体は使用しつつ、その内部でキーを生成するロジックのバリエーションを指します。
-
データに存在する一意のIDを使用する(最も推奨される方法) これが最もパフォーマンスが高く、安定した方法です。データがAPIなどから提供される場合、通常は一意の識別子(
id
、_id
、uuid
、productId
など)が含まれています。<FlatList data={myItems} renderItem={({ item }) => <MyItem item={item} />} keyExtractor={(item) => item.id.toString()} // もしくは item._id, item.uuid など />
- データオブジェクト自体に存在する、変更されない一意の値をキーとして使います。
- 数値の場合は
.toString()
で文字列に変換します。
-
インデックスをキーとして使用する(非推奨だが、限定的な状況で可) データが静的で、リストの要素が追加、削除、または並び替えられることが絶対にない場合にのみ、インデックスをキーとして使用することが許容されます。動的なリストでこれを行うと、パフォーマンスの問題や、UIのバグ(例:入力値がリセットされる)を引き起こす可能性があります。
<FlatList data={myStaticItems} renderItem={({ item, index }) => <MyItem item={item} />} keyExtractor={(item, index) => index.toString()} />
解説
keyExtractor
の第二引数index
を使用します。- インデックスは数値なので、必ず
toString()
で文字列に変換します。 - 本当にこれが静的なリストなのか、よく考えてから使用してください。
-
複数のプロパティを組み合わせて複合キーを生成する データに単一の一意なIDがない場合、複数のプロパティを連結して一意なキーを作成することができます。
const myData = [ { type: 'fruit', name: 'apple' }, { type: 'fruit', name: 'banana' }, { type: 'vegetable', name: 'carrot' }, ]; <FlatList data={myData} renderItem={({ item }) => <MyItem item={item} />} keyExtractor={(item) => `<span class="math-inline">\{item\.type\}\-</span>{item.name}`} // typeとnameを組み合わせる />
解説
- この方法は、組み合わせるプロパティが結果として一意になることを保証できる場合にのみ有効です。上記の例では、「apple」という名前がフルーツと野菜の両方に存在しないことを保証する必要があります。
-
キー生成関数を
useCallback
でメモ化する これはkeyExtractor
のロジック自体ではなく、keyExtractor
関数のパフォーマンスを最適化するための方法です。関数コンポーネントでkeyExtractor
をインラインで定義すると、親コンポーネントが再レンダリングされるたびにkeyExtractor
関数も再作成されてしまいます。これを避けるためにuseCallback
でメモ化します。import React, { useCallback } from 'react'; import { FlatList } from 'react-native'; const MyList = () => { const myData = [{ id: '1', name: 'Test' }]; const memoizedKeyExtractor = useCallback( (item) => item.id.toString(), [] // 依存配列が空なので、一度だけ作成される ); return ( <FlatList data={myData} renderItem={({ item }) => <MyItem item={item} />} keyExtractor={memoizedKeyExtractor} /> ); };
解説
useCallback
を使用することで、memoizedKeyExtractor
関数は依存配列[]
が変更されない限り再生成されません。これにより、FlatList
の内部最適化がより効果的に機能します。
もし「FlatList
のkeyExtractor
に関する代替方法」が、FlatList
自体を使わない場合の代替手段を指しているのであれば、以下の方法が挙げられます。
-
ScrollView
とmap()
関数を使用する これは最も基本的な方法で、配列のmap()
関数を使って各要素をレンダリングし、それらをScrollView
でラップします。import React from 'react'; import { ScrollView, Text, View, StyleSheet } from 'react-native'; const DATA = [ { id: '1', title: 'アイテムA' }, { id: '2', title: 'アイテムB' }, { id: '3', title: 'アイテムC' }, // ... ]; const App = () => { return ( <ScrollView style={styles.container}> {DATA.map((item) => ( // ここでReactのキーが必要になります <View key={item.id} style={styles.item}> <Text style={styles.title}>{item.title}</Text> </View> ))} </ScrollView> ); }; const styles = StyleSheet.create({ container: { flex: 1, paddingTop: 22, }, item: { backgroundColor: '#e0e0e0', padding: 20, marginVertical: 8, marginHorizontal: 16, }, title: { fontSize: 18, }, }); export default App;
注意点
ScrollView
は、すべてのアイテムを一度にレンダリングします。そのため、アイテム数が少ない(数十個程度まで)場合は問題ありませんが、大量のアイテム(数百、数千など)を扱う場合は、メモリ使用量とパフォーマンスが悪化します。map()
関数でレンダリングする各要素には、必ず一意のkey
プロップを設定する必要があります。これはFlatList
のkeyExtractor
が内部で行っていることと同様の目的です。
-
SectionList
を使用する もしデータがセクションに分かれている場合、SectionList
はより適切な選択肢です。SectionList
もFlatList
と同様に、キー抽出のためにkeyExtractor
(またはデフォルトのkey
/id
)を使用します。import React from 'react'; import { SectionList, Text, View, StyleSheet } from 'react-native'; const SECTIONS = [ { title: 'フルーツ', data: [{ id: 'f1', name: 'リンゴ' }, { id: 'f2', name: 'バナナ' }], }, { title: '野菜', data: [{ id: 'v1', name: 'ニンジン' }, { id: 'v2', name: 'タマネギ' }], }, ]; const App = () => { return ( <SectionList sections={SECTIONS} keyExtractor={(item, index) => item.id + index} // ここもkeyExtractorが必要 renderItem={({ item }) => ( <View style={styles.item}> <Text>{item.name}</Text> </View> )} renderSectionHeader={({ section: { title } }) => ( <Text style={styles.header}>{title}</Text> )} /> ); }; const styles = StyleSheet.create({ item: { padding: 10, marginVertical: 4, marginHorizontal: 16, backgroundColor: '#f0f0f0', }, header: { fontSize: 24, backgroundColor: '#fff', padding: 10, }, }); export default App;
解説
SectionList
は内部的にVirtualizedList
を使用しており、FlatList
と同様の仮想化によるパフォーマンス最適化が行われます。- ここでも、
keyExtractor
は各アイテム(セクション内のデータ)に対して一意のキーを生成するために使用されます。
FlatList
のkeyExtractor
は、React Nativeにおける大規模リスト表示のパフォーマンスと安定性を確保するための中心的な要素であり、これを使わないこと自体は推奨されません。むしろ、keyExtractor
をどのように効果的に使うかが重要になります。