Rork にプロンプトを入れてアプリを作り直したら、さっきまで動いていたリスト画面が急に真っ白になった—そんな経験はないでしょうか。console.log(data) を入れても配列は取れています。FlatList に渡しているのに、画面には何も出てきません。個人的にはこれが React Native 系でいちばんハマりやすい現象だと思っています。
FlatList が空で描画される原因はかなり限られていて、落ち着いて順番に切り分ければ 15 分以内には直せるはずです。ここでは私が Rork で実際に踏んで直してきたチェック順序を、そのまま書き出していきます。
まず疑うのは「描画領域が 0」かどうか
FlatList が真っ白になるケースのうち、体感で 6〜7 割はこれです。FlatList は ScrollView の派生なので、親の高さが確定していない状態で flex: 1 が効いていないと、高さ 0 の空間に全アイテムを押し込もうとして何も見えなくなります。
Rork の AI が生成したコードだと、ラップしている View に flex: 1 が付いていなかったり、途中に ScrollView が挟まっていたりすることがよくあります。
// ❌ これだと何も表示されない
<View>
<FlatList
data={items}
renderItem={({ item }) => <Text>{item.title}</Text>}
keyExtractor={(item) => item.id}
/>
</View>
// ✅ 親に flex:1、もしくは明示的な高さが必要
<View style={{ flex: 1 }}>
<FlatList
data={items}
renderItem={({ item }) => <Text>{item.title}</Text>}
keyExtractor={(item) => item.id}
/>
</View>もし画面全体の縦スクロールが欲しくて ScrollView で囲みたくなっても、ScrollView の中に FlatList を入れると仮想化が壊れて高さ計算が崩れます。こちらも真っ白の典型原因です。スクロールは FlatList 自身に任せて、ヘッダーやフッターが必要なら ListHeaderComponent と ListFooterComponent を使う形に書き換えましょう。
data 配列の中身を、型まで疑う
「data には入っているはず」と思って console.log を見ても、実は undefined や オブジェクトそのものが入っているケースが意外にあります。特に Supabase や外部 API のレスポンスをそのまま渡したときに起きやすい現象です。
const { data: items, error } = await supabase.from('posts').select('*');
// ❌ items が null の可能性がある(初回レンダリング時)
<FlatList data={items} ... />
// ✅ null / undefined を配列として扱う
<FlatList data={items ?? []} ... />また、Supabase の RLS(Row Level Security)で弾かれている場合はエラーなしで空配列が返ってきます。error の中身と、console.log(items?.length) の両方を確認するのが確実です。
もう一つありがちなのが、データ取得 Hook の戻り値が { data: [...] } のような形をしていて、それをそのまま渡してしまうパターンです。
// ❌ フックが返すオブジェクト全体を渡している
const response = useQuery(...);
<FlatList data={response} ... />
// ✅ 配列だけを取り出す
<FlatList data={response?.data ?? []} ... />renderItem と keyExtractor の書き方を見直す
FlatList に配列は渡っているのに、renderItem が空の View を返しているパターンも地味に多いです。
// ❌ item を分解していない(item は { item: ..., index: ... } オブジェクト)
<FlatList
data={items}
renderItem={(item) => <Text>{item.title}</Text>}
/>
// ✅ 第一引数を分割代入する
<FlatList
data={items}
renderItem={({ item }) => <Text>{item.title}</Text>}
/>renderItem の第一引数は { item, index, separators } というオブジェクトで、直接 item として扱うと undefined になります。React Native を触り始めた方がいちばん引っかかる箇所かもしれません。
keyExtractor も同様で、一意でない値や undefined を返していると、開発ビルドでは警告だけで済みますが、本番ビルドではリストが再描画されなかったり、アイテムが重なって描画されたりします。
// ❌ id が数値の場合、そのまま返すと型不一致でエラー
keyExtractor={(item) => item.id}
// ✅ 必ず文字列に変換する
keyExtractor={(item) => String(item.id)}横スクロール FlatList が消える場合
縦 FlatList は出るのに、horizontal を付けた瞬間に何も表示されなくなる—これもよくあります。原因は同じで、親の幅と高さが決まっていないためです。
// ❌ 高さ指定がないので潰れる
<FlatList
horizontal
data={items}
renderItem={({ item }) => <Card item={item} />}
/>
// ✅ 高さを明示するか、flex で確保する
<View style={{ height: 200 }}>
<FlatList
horizontal
data={items}
renderItem={({ item }) => <Card item={item} />}
showsHorizontalScrollIndicator={false}
/>
</View>横スクロールの場合、親要素に height を数値で指定するのが一番確実です。アイテム側で height: '100%' を書いても、親が未確定だと計算できません。
条件付き表示で FlatList ごと消えていないか
これは私も本番で一度ハマりました。items.length > 0 && のような条件で FlatList 自体をレンダリングしていると、データ取得中は一瞬 0 件になり、FlatList が DOM から消えて再マウントされることがあります。その結果、データが入ったタイミングでアニメーションが走らずに真っ白に見えたり、ローディング状態のちらつきが大きくなったりします。
// ❌ 0件のときに FlatList ごと消える
{items.length > 0 && (
<FlatList data={items} ... />
)}
{items.length === 0 && <Text>データがありません</Text>}
// ✅ FlatList は常に描画し、空状態は ListEmptyComponent で処理する
<FlatList
data={items}
renderItem={({ item }) => <Row item={item} />}
keyExtractor={(item) => String(item.id)}
ListEmptyComponent={
loading
? <ActivityIndicator style={{ marginTop: 40 }} />
: <Text style={{ textAlign: 'center', marginTop: 40 }}>データがありません</Text>
}
/>ListEmptyComponent を使うと、空状態・ロード中・エラー時の UI を FlatList の中で統一的に扱えます。再マウントも発生しないので、パフォーマンス面でも有利です。
デバッグに使える小さなテクニック
どうしても原因が分からないときは、以下を順番に試すと切り分けが速くなります。
data={[{id: '1', title: 'test'}]}のように固定値を渡してみる。これで表示されれば、データ取得側の問題と断定できます。renderItem={({ item }) => <Text style={{ color: 'red', fontSize: 40 }}>X</Text>}のようにスタイル最大で書いてみる。これで表示されれば、元のrenderItemの中のレイアウト問題です。- FlatList に
style={{ borderWidth: 2, borderColor: 'red' }}を付けて、描画領域の形を目視します。枠が線一本になっていれば高さ 0、枠が巨大なら中のアイテム側の問題です。 onLayoutで実際のサイズをログに出す:onLayout={(e) => console.log(e.nativeEvent.layout)}。width が 0 や height が 0 なら親のレイアウトが原因です。
Rork の AI にリペアを依頼するときも、「FlatList が表示されない」ではなく「FlatList は data.length=5 でデータは入っているが画面は空白。親 View の高さが 0 の可能性」のように症状と仮説をセットで書くと、修正精度が一気に上がります。AI に対しても、人間に相談するときと同じように情報を整理するイメージです。
合わせて読んでおきたい記事
FlatList の空白表示は単独のバグに見えて、レイアウト全体の設計不足が遠因になっていることも多いです。あわせて以下の記事も参考にしていただけると、似た現象を未然に防げます。
React Native / Expo のレイアウトモデルをもう少し体系的に
全体を振り返って
まずは今開いているそのコードに戻って、FlatList の親要素に flex: 1 か明示的な height が入っているかだけ確認してみてください。真っ白問題のかなりの割合がこれで直ります。そこを押さえたうえで、data の中身と renderItem の分割代入をチェックすれば、今後はリスト崩れに悩む時間がずいぶん短くなるはずです。