React Native FlatList renderItemで画像や複雑なレイアウトを表示する

2025-05-31

renderItem の役割

FlatList は、与えられたデータの配列に基づいてリストを表示しますが、それぞれのデータ要素をどのように画面に表示するかは renderItem 関数によって決定されます。この関数は、データ配列の各アイテムに対応する React コンポーネントまたは JSX 要素を返す必要があります。

renderItem 関数の構造

renderItem 関数は、通常、以下のような構造を持ちます。

renderItem={({ item, index, separators }) => {
  // ここに各アイテムの描画ロジックを記述します
  return (
    // 表示したいコンポーネントまたはJSX
    <View>
      <Text>{item.name}</Text>
      <Text>価格: {item.price}円</Text>
      {/* その他の表示要素 */}
    </View>
  );
}}

引数について

renderItem 関数は、オブジェクトを引数として受け取ります。このオブジェクトには、以下のプロパティが含まれています。

  • separators: アイテム間の区切り線を制御するためのいくつかの関数を含むオブジェクトです。主に、ハイライトや非ハイライトの表示に使用されます。

    • separators.highlight(): 区切り線をハイライト表示します。
    • separators.unhighlight(): 区切り線のハイライト表示を解除します。
    • separators.updateProps('leading' | 'trailing', newProps): 先頭または末尾の区切り線のプロパティを更新します。
  • index: 現在のアイテムの配列内でのインデックス(位置)です。0から始まる整数値です。

  • item: 現在描画しようとしているデータ配列の要素そのものです。例えば、データがオブジェクトの配列であれば、この item はその個々のオブジェクトになります。

具体的な使用例

例えば、以下のようなデータ配列があるとします。

const products = [
  { id: '1', name: 'リンゴ', price: 100 },
  { id: '2', name: 'バナナ', price: 150 },
  { id: '3', name: 'オレンジ', price: 200 },
];

このデータを FlatList で表示する場合、renderItem は以下のようになります。

<FlatList
  data={products}
  renderItem={({ item }) => (
    <View style={{ padding: 10, borderBottomWidth: 1, borderColor: '#ccc' }}>
      <Text style={{ fontWeight: 'bold' }}>{item.name}</Text>
      <Text>価格: {item.price}円</Text>
    </View>
  )}
  keyExtractor={(item) => item.id}
/>

この例では、renderItem は各商品オブジェクト (item) から nameprice を取り出し、Text コンポーネントで表示しています。各アイテムは View で囲まれ、下部に区切り線が表示されるようにスタイルが設定されています。

重要なポイント

  • renderItem が返すコンポーネントは、スタイルやイベントハンドラなど、通常の React コンポーネントと同様に扱うことができます。
  • separators を使用することで、アイテム間の区切り線の表示を細かく制御できます。
  • index を利用して、偶数行と奇数行で異なるスタイルを適用するなど、条件に応じた描画が可能です。
  • item プロパティを通じて、そのアイテムのデータにアクセスできます。
  • renderItem は、リストの各アイテムに対して呼び出されます。


renderItem が React 要素を返さない

エラーの状況
renderItem 関数内で return ステートメントを忘れたり、nullundefined など、React が描画できない値を返したりする場合に発生します。

エラーメッセージの例
特に明確なエラーメッセージが出ないこともありますが、リストが何も表示されない、または予期しない動作をすることがあります。

トラブルシューティング

  • 条件分岐などで nullundefined を返す可能性がある場合は、それらのケースで適切な代替コンポーネントを返すように修正してください。
  • return する値が、<View><Text> などの有効な React コンポーネントまたは JSX 要素であることを確認してください。
  • renderItem 関数内に必ず return ステートメントがあるか確認してください。

例(誤り)

renderItem={({ item }) => {
  // return を忘れている
  <Text>{item.name}</Text>
}}

例(修正)

renderItem={({ item }) => {
  return <Text>{item.name}</Text>;
}}

item プロパティの誤った使用

エラーの状況
renderItem の引数である { item } から、存在しないプロパティにアクセスしようとする場合に発生します。

エラーメッセージの例
「undefined is not an object (evaluating 'item.propertyName')」のようなエラーメッセージが表示されることがあります。

トラブルシューティング

  • データ構造が複雑な場合や、API から取得したデータを使用している場合は、データの形式を console.log などで確認し、renderItem で正しくアクセスできるように調整してください。
  • renderItem 内で item オブジェクトのプロパティにアクセスする際に、スペルミスがないか確認してください。
  • FlatList に渡している data 配列の要素構造(各オブジェクトのプロパティ名)を正確に把握してください。

例(誤り - 存在しないプロパティ 'title' にアクセス)

const data = [{ name: 'リンゴ' }, { name: 'バナナ' }];
// ...
renderItem={({ item }) => <Text>{item.title}</Text>}

例(修正)

const data = [{ name: 'リンゴ' }, { name: 'バナナ' }];
// ...
renderItem={({ item }) => <Text>{item.name}</Text>}

keyExtractor の未定義または不適切な定義

エラーの状況
FlatList は、リストのアイテムを効率的に再描画するために、各アイテムに一意のキーが必要です。keyExtractor プロパティが定義されていないか、一意でない値を返す関数が指定されている場合に、パフォーマンスの問題や予期しないレンダリングの挙動が発生することがあります。

エラーメッセージの例
特にエラーメッセージは表示されないことが多いですが、リストの更新が遅い、アイテムの順序が入れ替わるなどの問題が起こり得ます。

トラブルシューティング

  • インデックスを keyExtractor に使用することは、アイテムの順序が変わる場合に問題を引き起こす可能性があるため、避けるべきです。
  • keyExtractor には、各アイテムを一意に識別できる値を文字列として返す関数を指定してください。通常は、アイテムの id プロパティなどを使用します。
  • FlatList には必ず keyExtractor プロパティを指定してください。

例(誤り - keyExtractor が未定義)

<FlatList
  data={data}
  renderItem={({ item }) => <Text>{item.name}</Text>}
/>

例(修正 - id プロパティを一意のキーとして使用)

const data = [{ id: '1', name: 'リンゴ' }, { id: '2', name: 'バナナ' }];
// ...
<FlatList
  data={data}
  renderItem={({ item }) => <Text>{item.name}</Text>}
  keyExtractor={(item) => item.id}
/>

renderItem 内での複雑すぎるロジックやパフォーマンスの問題

エラーの状況
renderItem 内で非常に複雑な計算や処理を行うと、リストのスクロールが遅くなったり、アプリケーションの応答性が悪化したりする可能性があります。

トラブルシューティング

  • 必要に応じて、shouldComponentUpdate (クラスコンポーネントの場合) や React.memo (関数コンポーネントの場合) を使用して、不必要な再レンダリングを防ぐことを検討してください。
  • 複雑なロジックは、renderItem の外で事前に計算したり、別のヘルパー関数に分離したりすることを検討してください。
  • renderItem 内では、主に描画に必要な処理のみを行うように心がけてください。

スタイルの適用に関する問題

エラーの状況
renderItem 内で定義したスタイルが意図通りに適用されない場合があります。

トラブルシューティング

  • StyleSheet.create を使用してスタイルを定義し、再利用することを推奨します。
  • 親コンポーネントや他のスタイルの影響を受けていないか確認してください。スタイルの優先順位や継承関係を考慮する必要があります。
  • スタイルオブジェクトのプロパティ名が正しいか、値が適切かを確認してください。

separators の誤った使用

エラーの状況
renderItem の引数に含まれる separators オブジェクトの関数 (highlight, unhighlight, updateProps) を誤って使用すると、区切り線の表示が意図しないものになることがあります。

トラブルシューティング

  • separators.updateProps() を使用する場合は、更新したい区切り線の種類 ('leading' または 'trailing') と、適用したい新しいプロパティを正しく指定してください。
  • separators.highlight() および separators.unhighlight() は、区切り線の表示状態を一時的に変更するための関数です。適切なタイミングで使用してください(例えば、アイテムが押されたとき)。

条件付きレンダリングのミス

エラーの状況
renderItem 内で条件に基づいて異なるコンポーネントをレンダリングする場合、条件式の記述ミスや、すべての条件に対応するレンダリング処理が記述されていない場合に、予期しない表示になることがあります。

  • if-else 文や三項演算子 (? :) を使用して、すべての可能性のある条件に対応したレンダリング処理を記述してください。
  • 条件式が意図した通りに評価されるか、console.log などで確認してください。


例1: 基本的なテキスト表示

最も基本的な例として、シンプルなテキストのリストを表示するケースです。

import React from 'react';
import { FlatList, StyleSheet, Text, View } from 'react-native';

const data = [
  { id: '1', title: '最初のアイテム' },
  { id: '2', title: '2番目のアイテム' },
  { id: '3', title: '3つ目のアイテム' },
];

const Item = ({ title }) => (
  <View style={styles.item}>
    <Text style={styles.title}>{title}</Text>
  </View>
);

const App = () => {
  const renderItem = ({ item }) => (
    <Item title={item.title} />
  );

  return (
    <View style={styles.container}>
      <FlatList
        data={data}
        renderItem={renderItem}
        keyExtractor={item => item.id}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 22,
  },
  item: {
    backgroundColor: '#f9c2ff',
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
  },
  title: {
    fontSize: 24,
  },
});

export default App;

解説

  • keyExtractor: 各アイテムに一意のキーを提供するための関数です。ここでは、各アイテムの id をキーとして使用しています。
  • renderItem: FlatListrenderItem プロパティに渡される関数です。
    • 引数として { item } を受け取ります。これは、data 配列の現在の要素です。
    • <Item title={item.title} /> を返すことで、各データ要素に対応した Item コンポーネントをレンダリングしています。
  • Item コンポーネント: 個々のアイテムを描画するシンプルなコンポーネントです。title プロパティを受け取り、Text で表示します。
  • data: 表示するデータの配列です。各オブジェクトは idtitle プロパティを持っています。

例2: 画像とテキストの表示

より複雑な例として、データに画像情報も含まれている場合に、画像とテキストを組み合わせて表示するケースです。

import React from 'react';
import { FlatList, StyleSheet, Text, View, Image } from 'react-native';

const data = [
  { id: '1', name: '猫', imageUrl: 'https://placekitten.com/50/50' },
  { id: '2', name: '犬', imageUrl: 'https://placedog.com/50/50' },
  { id: '3', name: '鳥', imageUrl: 'https://via.placeholder.com/50' },
];

const Item = ({ name, imageUrl }) => (
  <View style={styles.item}>
    <Image source={{ uri: imageUrl }} style={styles.image} />
    <Text style={styles.name}>{name}</Text>
  </View>
);

const App = () => {
  const renderItem = ({ item }) => (
    <Item name={item.name} imageUrl={item.imageUrl} />
  );

  return (
    <View style={styles.container}>
      <FlatList
        data={data}
        renderItem={renderItem}
        keyExtractor={item => item.id}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 22,
  },
  item: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#e0f7fa',
    padding: 10,
    marginVertical: 8,
    marginHorizontal: 16,
  },
  image: {
    width: 50,
    height: 50,
    marginRight: 10,
  },
  name: {
    fontSize: 18,
  },
});

export default App;

解説

  • renderItem: item オブジェクトから nameimageUrl を取り出し、Item コンポーネントに props として渡しています。
  • Item コンポーネント: Image コンポーネントを使用して、imageUrl から画像を読み込んで表示しています。flexDirection: 'row'alignItems: 'center' を使用して、画像とテキストを横並びに配置しています。
  • data: 各オブジェクトに nameimageUrl プロパティが追加されました。

例3: インデックスを利用した条件付きレンダリング

renderItem の引数には index も含まれています。これを利用して、リストの偶数行と奇数行で異なるスタイルを適用する例です。

import React from 'react';
import { FlatList, StyleSheet, Text, View } from 'react-native';

const data = Array.from({ length: 10 }, (_, i) => ({ id: String(i), value: `アイテム ${i + 1}` }));

const Item = ({ value, index }) => (
  <View style={[styles.item, index % 2 === 0 ? styles.evenItem : styles.oddItem]}>
    <Text style={styles.value}>{value}</Text>
  </View>
);

const App = () => {
  const renderItem = ({ item, index }) => (
    <Item value={item.value} index={index} />
  );

  return (
    <View style={styles.container}>
      <FlatList
        data={data}
        renderItem={renderItem}
        keyExtractor={item => item.id}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 22,
  },
  item: {
    padding: 20,
    marginVertical: 8,
    marginHorizontal: 16,
  },
  evenItem: {
    backgroundColor: '#bbdefb', // 偶数行の色
  },
  oddItem: {
    backgroundColor: '#e1f5fe',  // 奇数行の色
  },
  value: {
    fontSize: 16,
  },
});

export default App;

解説

  • Item コンポーネント: index プロパティを受け取り、index % 2 === 0 で偶数行かどうかを判定し、条件に応じて異なるスタイル (styles.evenItem または styles.oddItem) を適用しています。
  • renderItem: 引数として { item, index } を受け取ります。

例4: separators を使用した区切り線のカスタマイズ

renderItem の引数には separators オブジェクトも含まれています。これを利用して、アイテムがハイライトされたときに区切り線のスタイルを変更する例です。

import React from 'react';
import { FlatList, StyleSheet, Text, View, TouchableHighlight } from 'react-native';

const data = Array.from({ length: 5 }, (_, i) => ({ id: String(i), text: `アイテム ${i + 1}` }));

const Item = ({ item, index, separators }) => (
  <TouchableHighlight
    onPress={() => {
      separators.highlight();
      setTimeout(() => separators.unhighlight(), 1000); // 1秒後にハイライトを解除
    }}
    underlayColor="lightgray"
  >
    <View style={styles.item}>
      <Text style={styles.text}>{item.text}</Text>
    </View>
  </TouchableHighlight>
);

const App = () => {
  const renderItem = ({ item, index, separators }) => (
    <Item item={item} index={index} separators={separators} />
  );

  const ItemSeparator = () => (
    <View style={styles.separator} />
  );

  return (
    <View style={styles.container}>
      <FlatList
        data={data}
        renderItem={renderItem}
        keyExtractor={item => item.id}
        ItemSeparatorComponent={ItemSeparator}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    paddingTop: 22,
  },
  item: {
    backgroundColor: '#fff',
    padding: 20,
  },
  text: {
    fontSize: 16,
  },
  separator: {
    height: 1,
    backgroundColor: '#ccc',
  },
});

export default App;
  • ItemSeparatorComponent: FlatList のプロパティで、アイテム間の区切り線を描画するコンポーネントを指定します。ここではシンプルな横線を表示する ItemSeparator コンポーネントを使用しています。
  • Item コンポーネント: TouchableHighlight でラップし、onPress イベントで separators.highlight() を呼び出して区切り線をハイライト表示し、setTimeout 後に separators.unhighlight() で解除しています。
  • renderItem: 引数として { item, index, separators } を受け取ります。


関数コンポーネントを直接 renderItem に記述する (インラインレンダリング)

renderItem の内部で、個別のコンポーネントを定義せずに直接 JSX を記述する方法です。シンプルな表示の場合に便利ですが、ロジックが複雑になると可読性が低下する可能性があります。

import React from 'react';
import { FlatList, StyleSheet, Text, View } from 'react-native';

const data = [
  { id: '1', name: 'リンゴ' },
  { id: '2', name: 'バナナ' },
];

const App = () => {
  return (
    <View style={styles.container}>
      <FlatList
        data={data}
        renderItem={({ item }) => (
          <View style={styles.item}>
            <Text style={styles.name}>{item.name}</Text>
          </View>
        )}
        keyExtractor={item => item.id}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, paddingTop: 20 },
  item: { padding: 10, borderBottomWidth: 1, borderColor: '#ccc' },
  name: { fontSize: 16 },
});

export default App;

解説

  • シンプルな表示であれば可読性も保たれますが、複雑なロジックやスタイルが絡む場合は、可読性を損なう可能性があります。
  • Item コンポーネントを別途定義する必要がないため、コードが短くなります。
  • renderItem プロパティに、アロー関数として直接 JSX を記述しています。

高階関数を利用して renderItem を生成する

アイテムの種類や状態に応じて異なるレンダリング処理を行いたい場合に、高階関数を使って renderItem を動的に生成する方法です。

import React from 'react';
import { FlatList, StyleSheet, Text, View } from 'react-native';

const data = [
  { id: '1', type: 'text', value: 'テキストアイテム' },
  { id: '2', type: 'number', value: 123 },
];

const renderItemByType = () => ({ item }) => {
  switch (item.type) {
    case 'text':
      return (
        <View style={styles.item}>
          <Text>{item.value}</Text>
        </View>
      );
    case 'number':
      return (
        <View style={styles.item}>
          <Text style={styles.number}>{item.value}</Text>
        </View>
      );
    default:
      return null;
  }
};

const App = () => {
  return (
    <View style={styles.container}>
      <FlatList
        data={data}
        renderItem={renderItemByType()}
        keyExtractor={item => item.id}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, paddingTop: 20 },
  item: { padding: 10, borderBottomWidth: 1, borderColor: '#ccc' },
  number: { fontWeight: 'bold', color: 'blue' },
});

export default App;

解説

  • これにより、アイテムの種類に応じたレンダリングロジックを renderItem の外で管理できます。
  • 内部の関数は、itemtype プロパティに基づいて異なる JSX を返します。
  • renderItemByType という関数は、別の関数(renderItem として FlatList に渡される関数)を返します。

React.memo を使用したコンポーネントの最適化

renderItem でレンダリングされるコンポーネントが、props の変更がない限り再レンダリングされるのを防ぐために React.memo を使用する方法です。リストのパフォーマンス向上に役立ちます。

import React from 'react';
import { FlatList, StyleSheet, Text, View } from 'react-native';

const data = [
  { id: '1', name: 'リンゴ' },
  { id: '2', name: 'バナナ' },
];

const Item = React.memo(({ name }) => (
  <View style={styles.item}>
    <Text style={styles.name}>{name}</Text>
  </View>
));

const App = () => {
  const renderItem = ({ item }) => (
    <Item name={item.name} />
  );

  return (
    <View style={styles.container}>
      <FlatList
        data={data}
        renderItem={renderItem}
        keyExtractor={item => item.id}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, paddingTop: 20 },
  item: { padding: 10, borderBottomWidth: 1, borderColor: '#ccc' },
  name: { fontSize: 16 },
});

export default App;

解説

  • リストのアイテム数が多く、各アイテムのレンダリングコストが高い場合に、パフォーマンスの改善が期待できます。
  • これにより、Item コンポーネントは、受け取る name プロパティが前回のレンダリング時と異なる場合にのみ再レンダリングされます。
  • Item コンポーネントを React.memo() でラップしています。

Render Props パターン

Render Props パターンを利用して、アイテムのデータを受け取り、どのようにレンダリングするかを親コンポーネントから提供する方法です。より柔軟なコンポーネント設計が可能になります。

import React from 'react';
import { FlatList, StyleSheet, View } from 'react-native';

const data = [
  { id: '1', value: 'テキストA' },
  { id: '2', value: 'テキストB' },
];

const MyListItemRenderer = ({ item }) => (
  <View style={styles.item}>
    <Text style={styles.text}>{item.value}</Text>
  </View>
);

const App = ({ listItemRenderer }) => {
  const renderItem = ({ item }) => listItemRenderer({ item });

  return (
    <View style={styles.container}>
      <FlatList
        data={data}
        renderItem={renderItem}
        keyExtractor={item => item.id}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, paddingTop: 20 },
  item: { padding: 10, borderBottomWidth: 1, borderColor: '#ccc' },
  text: { fontSize: 16 },
});

const UsageExample = () => (
  <App listItemRenderer={MyListItemRenderer} />
);

export default UsageExample;
  • これにより、リストのアイテムのレンダリング方法を、FlatList を使用する親コンポーネント側で定義できます。
  • UsageExample コンポーネントでは、実際に MyListItemRenderer コンポーネントを listItemRenderer prop として App に渡しています。
  • renderItem 関数内で、この listItemRenderer を呼び出し、現在の item を引数として渡します。
  • App コンポーネントは、listItemRenderer という関数 prop を受け取ります。