react / react/react-native

Nested horizontal FlatList reports items as viewable while its parent row is outside the screen

オープン
#57,778 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

Component: FlatList Needs: Author Feedback Needs: Repro
主要言語
C++
スター
127k
フォーク
25.3k
平均マージ
1日 23時間
マージ済み PR(30日)
4

説明

Description

System Info
React Native: 0.86.2
React: 19.2.3
React Native Gradle Plugin: 0.86.2
Node: 22.22.3
Project Node version (.nvmrc): 22.22.3
npm: 10.9.8
Yarn: 3.5.0
OS: Linux x86_64, kernel 6.8.0-136-generic
Android compile SDK: 36
Android target SDK: 36
Android min SDK: 24 (Android 7.0+)
Java: 17.0.19
Hermes: enabled
New Architecture: enabled

Expected behavior
For a horizontal FlatList nested inside a vertically scrolling FlatList, the inner list’s onViewableItemsChanged should not report an item as viewable until
the parent row is visible in the device viewport.

An item should satisfy both conditions:

Visible within the inner horizontal FlatList
AND
The parent row containing that FlatList is visible on screen

Actual behavior
onViewableItemsChanged for a nested horizontal FlatList fires when its parent row mounts, even if the parent row is below the device viewport.

The inner FlatList appears to calculate viewability relative to its own local scroll viewport only. It does not account for the outer vertical FlatList’s
clipping or whether the parent row is actually visible on the screen.

This produces false impression events for analytics: items are marked viewable before the user can see them.

Details
Our application has a vertical FlatList of sections. Several sections contain horizontal FlatLists.

On the initial screen:

Sections 1–4: visible in the device viewport
Sections 5–8: mounted by the outer FlatList but below the device viewport

The nested horizontal FlatLists in Sections 5–8 invoke onViewableItemsChanged immediately, reporting their initial items as viewable before the user scrolls
the outer list.

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

const SECTION_HEIGHT = 220;
const ITEM_WIDTH = 140;

const sections = Array.from({ length: 8 }, (_, index) => ({
id: section-${index + 1},
title: Section ${index + 1},
}));

const items = Array.from({ length: 8 }, (_, index) => ({
id: item-${index + 1},
title: Item ${index + 1},
}));

function NestedHorizontalList({ sectionId }) {
// These objects must retain their identity for the lifetime of a FlatList.
const viewabilityConfig = useRef({
itemVisiblePercentThreshold: 50,
minimumViewTime: 0,
waitForInteraction: false,
}).current;

const onViewableItemsChanged = useRef(({ viewableItems }) => {
console.log(
[${sectionId}] reported viewable before outer list is scrolled:,
viewableItems.map(({ item }) => item.id).join(', ')
);
}).current;

return (
<FlatList
horizontal
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (

{item.title}

)}
viewabilityConfig={viewabilityConfig}
onViewableItemsChanged={onViewableItemsChanged}
showsHorizontalScrollIndicator={false}
/>
);
}

export default function App() {
return (
<FlatList
data={sections}
getItemLayout={(_, index) => ({
length: SECTION_HEIGHT,
offset: SECTION_HEIGHT * index,
index,
})}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (

{item.title}

)}
/>
);
}

const styles = StyleSheet.create({
section: {
height: SECTION_HEIGHT,
paddingTop: 16,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: '#9ca3af',
},
sectionTitle: {
marginHorizontal: 16,
marginBottom: 12,
fontSize: 18,
fontWeight: '600',
},
item: {
width: ITEM_WIDTH,
height: 120,
marginLeft: 12,
alignItems: 'center',
justifyContent: 'center',
borderRadius: 8,
backgroundColor: '#dbeafe',
},
});`

For analytics, this is incorrect: “viewable” should represent visibility to the user, not only visibility within an off-screen nested list container.

Steps to reproduce
  1. Reproduction steps
  2. Render a vertical FlatList with multiple fixed-height rows.
  3. Place a horizontal FlatList inside every row.
  4. Add onViewableItemsChanged and viewabilityConfig to each inner horizontal FlatList.
  5. Launch the screen without scrolling.
  6. Observe that inner lists belonging to below-the-fold parent rows report items as viewable.
React Native Version

0.86.2

Affected Platforms

Runtime - Android, Runtime - iOS

Output of npx @react-native-community/cli info
System:
  OS: Linux 6.8 Ubuntu 22.04.5 LTS 22.04.5 LTS (Jammy Jellyfish)
  CPU: (16) x64 12th Gen Intel(R) Core(TM) i7-1270P
  Memory: 13.94 GB / 30.59 GB
  Shell:
    version: 5.1.16
    path: /bin/bash
Binaries:
  Node:
    version: 22.22.3
    path: /home/user/.nvm/versions/node/v22.22.3/bin/node
  Yarn:
    version: 3.5.0
    path: /home/user/.nvm/versions/node/v22.22.3/bin/yarn
  npm:
    version: 10.9.8
    path: /home/user/.nvm/versions/node/v22.22.3/bin/npm

SDKs:
  Android SDK:
    API Levels: 28, 30, 31, 32, 33, 34, 35, 36
    Build Tools: 36.1.0
    System Images: android-36 | Google Play Intel x86_64 Atom
  Android NDK: Not required for standard Expo development

Languages:
  Java:
    version: 17.0.19

npmPackages:
  react: 19.2.3
  react-native: 0.86.2
  expo: ~57.0.7

Android:
  hermesEnabled: true
  newArchEnabled: true
Stacktrace or Logs
NA
MANDATORY Reproducer

https://snack.expo.dev/@testophobia/-nested-flatlist-viewability-repro

Screenshots and Videos

No response

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

必須の Snack 再現例と、viewabilityConfig および onViewableItemsChanged を使用するネストされた FlatList の設定から始めます。親の行がデバイスの viewport の外側にある場合に、内側の水平リストがどのように可視性を判定するかを追跡します。画面下部のネストされたリストがアイテムを可視として報告しなくなり、表示されているネストされたアイテムは Android と iOS で引き続き報告されれば完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
javascript, react-native
領域
mobile
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
66/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。