react-native: [iOS/iPadOS] - `FlatList` refresh handling broken/dysfunctional
Description
Description
Since upgrading to the new architecture it seems like the refresh/pull-to-refresh feature of the FlatList
is broken/not fully functional any longer. However, this is only the case if you place another FlatList
OR ScrollView
in the same view. Placing just a list in each view does not lead to problems.
Pulling down and refreshing works twice before being dysfunctional. Eventually, after a certain pattern, the functionality works again twice and then breaks again.
The sample code provided below works as intended on the old architecture.
Version
0.71.3
Output of npx react-native info
Not relevant for this issue.
Steps to reproduce
- Create a new
react-native
project with the help of the CLI, install all dependencies and pods and build the application on a iOS/iPadOS device. - Paste the provided code into the
App.tsx
file - Pull down the list and notice how the loading spinner is displayed for a brief time
- Use the
Switch
button to unmount the current view and mount another one containing twoFlatList
components and aScrollView
(for demonstration purposes). - Pull to refresh on the most left
FlatList
(orange one) and once again use theSwitch
button - Pull down the initial list and notice how the loading spinner is NOT displayed and the
onRefresh
handler is not called
Snack, code example, screenshot, or link to a repository
https://github.com/coolersham/NRNA-flatlist-reload-bug
import React, { useState } from "react"
import {
FlatList,
ScrollView,
Text,
TouchableOpacity,
View,
} from "react-native"
export default function App(): JSX.Element {
return (
<View
style={{
flex: 1,
padding: 48,
alignItems: "center",
backgroundColor: "white",
}}
>
<ListDemo />
</View>
)
}
function ListDemo() {
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const [show, setShow] = useState(true)
const style = { backgroundColor: "orange" }
const switchButton = (
<TouchableOpacity
style={{
height: 48,
width: 300,
alignItems: "center",
backgroundColor: "red",
justifyContent: "center",
}}
onPress={() => setShow((show) => !show)}
>
<Text style={{ color: "white" }}>Switch</Text>
</TouchableOpacity>
)
if (show)
return (
<>
{switchButton}
<FlatList
data={data}
style={style}
refreshing={false}
renderItem={GenericListItem}
onRefresh={() => console.log("List #1 refresh is called")}
/>
</>
)
return (
<>
{switchButton}
<View
style={{
flex: 1,
flexDirection: "row",
}}
>
<FlatList
data={data}
style={style}
refreshing={false}
renderItem={GenericListItem}
onRefresh={() => console.log("List #2 refresh is called")}
/>
{/* Breaks refresh handling of both lists occasionally */}
<FlatList
data={data}
renderItem={GenericListItem}
style={{ backgroundColor: "purple" }}
/>
{/* The same applies to the normal ScrollView component */}
<ScrollView style={{ backgroundColor: "blue" }}>
{data.map((item) => (
<GenericListItem key={item} />
))}
</ScrollView>
</View>
</>
)
}
function GenericListItem() {
return (
<View style={{ width: 300, height: 48 }}>
<Text>Generic test item</Text>
</View>
)
}
About this issue
- Original URL
- State: open
- Created a year ago
- Reactions: 2
- Comments: 25 (5 by maintainers)
Commits related to this issue
- Properly recycle RCTPullToRefreshViewcomponentView Summary: This change properly recycles the RCTPullToRefreshViewComponentView so that it fixes some misbehaviors of the UIRefreshControl in OSS. Thi... — committed to cipolleschi/react-native by cipolleschi 3 months ago
- Properly recycle RCTPullToRefreshViewcomponentView (#44047) Summary: This change properly recycles the RCTPullToRefreshViewComponentView so that it fixes some misbehaviors of the UIRefreshControl in... — committed to cipolleschi/react-native by cipolleschi 3 months ago
- Properly recycle RCTPullToRefreshViewcomponentView (#44047) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/44047 This change properly recycles the RCTPullToRefreshView... — committed to facebook/react-native by cipolleschi 3 months ago
@cortinico Here is the repository that reproduces this bug in the latest version of
react-native
(0.71.6).Again I kindly ask that someone from the core or new architecture team of
react-native
looks into this, please. Please just leave a comment, whether this issue will never be fixed or you do not have time for this/your priorities are spread differently, so that all people who are struggling with that issue know what the next steps will be.We found the cause of the issue, and it was because the RCTPullToRefreshViewComponentView was not recycled correctly. What happened is that the UIRefreshControl was not deallocated and remained assigned to a disposed ScrollView. By properly recreating it in prepare for recycle, we fixed the bug.
Hi there, Sorry for the long silence, but as @cortinico mentioned above, we receive tons of issues, the team is small and we need to prioritize ruthlessly.
I’ll start looking in to the issue today and for the next few days. Thanks @coolersham for the reproducer, I’ll try to keep you all updated on the progress.
Update 1:I can repro the problem. I also noticed another difference between the Old and the New Architecture:* in the Old Architecture, theUIActivityIndicator
is below the content view: when the content view is released, and it comes back to the top, it hides theUIActivityIndicator
.* in the New Architecture, theUIActivityIndicator
is in front of the content view: when the content view is released, and it comes back to the top,UIActivityIndicator
hides part of theListView
.☝️ this is caused by the reproducers that sets the
refreshing
to false and does not handle the state properly, so it is not a bug in Core.Please do 🙏 That would also help others which wish to investigate this
@coolersham we receive hundreds of issues every day and sadly our team is really small. We’re trying to do our best to look at all of them and provide answers.
I’ve looked at your issue and sadly I don’t have relevant context/suggestions to share at this stage.
@coolersham did you end up finding a way to solve this?
@carlosalmonte04 Thanks for looking into this and providing a possible workaround. However, as you mentioned, those approaches are disappointing and unsatisfactory.
I am not quite aware of how the responsibilities are divided in
react-native
matters, but could someone from the core or rather new architecture team look into this issue, please? Maybe I am doing something wrong or miss a piece of important information, but I think this functionality should not act like that. Even more, because it worked on the old architecture just fine.Maybe @cortinico or would you mind delegating? Thanks!
@carlosalmonte04 As stated in the main description of the issue, the problem is related to the
new
architecture. The template that the CLI retrieves in step 1. still uses theold
architecture as a default setting.You have to modify your fifth step to enable the new architecture and its corresponding parts. Instead of
pod install
doNO_FLIPPER=1 USE_FABRIC=1 USE_HERMES=1 RCT_NEW_ARCH_ENABLED=1 pod install
.Now you should be able to reproduce and perceive the bug. Please let me know if that has worked for you.