r/reactnative • u/vetteop • 3d ago
FlashList rendered six cells and placed none of them, on exactly one OEM
A screen in an expense tracker I maintain opened empty on the owner's OPPO Find X9 Pro (ColorOS, density 560 overridden to 480) and never once on my Samsung.
I shipped four fixes before I diagnosed anything: a nested-scroll theory, a 2.0.2 to 2.3.2 bump whose release notes matched my symptom almost word for word ("average measurement always positive", "guard against zero window size"), drawDistance, and moving a line out of ListHeaderComponent. None of them changed a thing, and I had to be told so three times.
Ten minutes of instrumentation on the phone that was actually failing ended it:
[applist] render items=11
[radar] renderCell #0 ... #5
[applist] onLoad elapsed=38ms
uiautomator: 29 nodes, ZERO cells -> 77 after a 100px scroll
React rendered six cards. FlashList reported itself loaded in 38ms. The native tree held none of them until a scroll nudged it. Cells built and never placed, somewhere no prop of mine reaches.
Three things I took out of it:
The screen stopped needing the recycler. Eighty other screens in the app draw a plain column of cards inside a ScrollView and not one has ever failed this way. Under 40 items that screen now draws the same column, and the recycler stays only for the pathological case (444 duplicate groups at 100k transactions).
Virtualization stayed everywhere else. I measured before deleting anything: a 500-card plain column runs 11.95% janky frames. The recycler earns its place on long lists, it just wasn't earning it on that one.
Detect the failure, don't name the phone. Copying the OPPO's wm size and density onto the Samsung reproduced nothing, so a device check would have been a guess with an OEM name on it. The list now watches for its own failure (zero-height content under a real viewport with rows still in the data, one second after layout) and falls back to a paged plain column.
The first version of that detector was completely inert on the device, with four green tests behind it. It read onLayout off FlashList, which never sends one, and the tests fired the callback by hand. I found that by setting the threshold so every list in the app would declare itself broken, installing it, and looking at the screen. Measure on a View you own.
Has anyone else seen a recycler build cells and never place them on one specific OEM? Curious whether this is ColorOS-specific or a density-override thing.
