ReactNative + TypeScript + React HooksでFlatList使うときにrenderItemの型でハマった時の解決方法


導入

TypeScript無しでは、FlatListを書いたことあるので、スラスラ書けるかなと思って、
TypeScriptでFlatListを書いていました。

しかし、TypeScriptのエラーに引っかかりまくり、なかなか解決できなかったので、
その解決方法を共有する目的で記事を書いています。

前提

VersionとFlatListについての前提情報を記載します。

Version

  • ReactNative: 0.59.8
  • Expo: 35.0.0

FlatListとは?

スクロールできるリストのこといいます。
ReactNativeで作成するアプリで、リスト形式で表示したい時は多くの場合利用すると思います。

ハマったこと

サンプルコードは以下の通りなのですが、ハマったのは、
FlatList内のrenderItemの型です。
TypeScriptを導入していなければ以下のサンプルコードのままでいいのですが、
TypeScriptを導入していると以下のコードでは怒られてしまいます。

修正前のコード

  import React from 'react';
  import { SafeAreaView, View, FlatList, StyleSheet, Text } from 'react-native';
  import Constants from 'expo-constants';

  const DATA = [
    {
      id: '1',
      title: 'アイテム1',
    },
    {
      id: '2',
      title: 'アイテム2',
    },
    {
      id: '3',
      title: 'アイテム3',
    },
    {
      id: '4',
      title: 'アイテム4',
    },
    {
      id: '5',
      title: 'アイテム5',
    },
    {
      id: '6',
      title: 'アイテム6',
    },
    {
      id: '7',
      title: 'アイテム7',
    },
  ];

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

  export default App = () => {
    return (
      <SafeAreaView style={styles.container}>
        <FlatList
          data={DATA}
          renderItem={({ item }) => <Item title={item.title} />}
          keyExtractor={item => item.id}
        />
      </SafeAreaView>
    );
  }

  const styles = StyleSheet.create({
    container: {
      flex: 1,
      marginTop: Constants.statusBarHeight,
    },
    item: {
      backgroundColor: 'red',
      padding: 20,
      marginVertical: 8,
      marginHorizontal: 16,
    },
    title: {
      fontSize: 32,
    },
  });

怒られたエラーメッセージ

Type ListRenderItemInf<any> is missing ...

ListRenderItemInfo<any>が存在しないよというエラーが発生しました。

解決方法

解決方法は、ListRenderItemInfo型をreact-naiveからimportしてきて、
FlatListのrenderItempropsの型として定義してあげる方法です。

修正後のコード


   import React from 'react';
  // ListRenderItemInfoをimportしてきます
  import { SafeAreaView, View, FlatList, StyleSheet, Text, ListRenderItemInfo } from 'react-native';
  import Constants from 'expo-constants';

  const DATA = [
    {
      id: '1',
      title: 'アイテム1',
    },
    {
      id: '2',
      title: 'アイテム2',
    },
    {
      id: '3',
      title: 'アイテム3',
    },
    {
      id: '4',
      title: 'アイテム4',
    },
    {
      id: '5',
      title: 'アイテム5',
    },
    {
      id: '6',
      title: 'アイテム6',
    },
    {
      id: '7',
      title: 'アイテム7',
    },
  ];

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

 // Itemはどのような型なのかを設定します。
  // interfaceなので慣習的にIをつけるので、IItemとします。
  export interface IItem{
    id: number;
    title: string;
  }

  // _renderItemというメソッドを作成します。
  // ListRenderItemInfoを型に設定します。
  const _renderItem = (listRenderItemInfo: ListRenderItemInfo<IItem>) => {
    return <Item title={listRenderItemInfo.item.title} />;
  };

  export default App = () => {
    return (
      <SafeAreaView style={styles.container}>
        <FlatList
          data={DATA}
          // 先程作成した_renderItemメソッドを当て込みます
          renderItem={_renderItem}
          keyExtractor={item => item.id}
        />
      </SafeAreaView>
    );
  }

  const styles = StyleSheet.create({
    container: {
      flex: 1,
      marginTop: Constants.statusBarHeight,
    },
    item: {
      backgroundColor: 'red',
      padding: 20,
      marginVertical: 8,
      marginHorizontal: 16,
    },
    title: {
      fontSize: 32,
    },
  });


まとめ

TypeScriptでは思わぬところで、ハマってしまうことが多いですよね。
renderItemにも型が決められていると気づくことができれば、一瞬で解決できるのですが、焦ってしまい少し時間がかかってしまいました。

また、ReactNativeの情報はまだ日本語で不足しているので、日本語では解決方法を見つけることができませんでした。
同じような構成でアプリを作成している皆さんの助けになれば幸いです。

参考

https://reactnative.dev/docs/flatlist
https://github.com/ranuseh/TraderJoeProjectFrontend/blob/e24a9fc5372356a24028c9029a00627d3e9fae3c/app/screens/shoppingList.screen.tsx