react-native: FlatList item onPress not work the first time after refreshed
- Review the documentation: https://facebook.github.io/react-native
- Search for existing issues: https://github.com/facebook/react-native/issues
- Use the latest React Native release: https://github.com/facebook/react-native/releases
Environment
React Native Environment Info:
System:
OS: macOS High Sierra 10.13.5
CPU: x64 Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
Memory: 25.63 MB / 8.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 8.11.2 - /usr/local/bin/node
Yarn: 1.7.0 - /usr/local/bin/yarn
npm: 5.6.0 - /usr/local/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
SDKs:
iOS SDK:
Platforms: iOS 11.4, macOS 10.13, tvOS 11.4, watchOS 4.3
IDEs:
Android Studio: 3.1 AI-173.4819257
Xcode: 9.4.1/9F2000 - /usr/bin/xcodebuild
npmPackages:
react: 16.3.1 => 16.3.1
react-native: 0.56.0 => 0.56.0
npmGlobalPackages:
create-react-native-app: 1.0.0
react-native-cli: 2.0.1
react-native-scripts: 1.14.0
Description
FlatList
has item with TouchableHighlight
, and a RefreshControl
attached.
onPress
method of TouchableHighlight
is not working the first time after onRefresh
called.
If I scroll FlatList
a bit after refreshed, then item onPress
works fine.
// UPDATE: Android does not have this bug.
Reproducible Demo
Fresh project created by react-native init
import React, { Component } from "react";
import { Text, View, FlatList, TouchableOpacity, RefreshControl } from "react-native";
type Props = {};
export default class App extends Component<Props> {
constructor() {
super();
this.state = { refreshing: true, items: [] };
}
componentDidMount() {
this.refresh();
}
genItems = () => [0, 1, 2, 3, 4, 5];
refresh = () => {
this.setState({ refreshing: true, items: [] });
setTimeout(() => this.setState({ refreshing: false, items: this.genItems() }), 1500);
};
renderItem = ({ item }) => {
const text = `${item}`;
return (
<TouchableOpacity onPress={() => alert("pressed!")}>
<Text style={{ width: "100%", height: 48, backgroundColor: "white" }}>
{text}
</Text>
<View style={{ width: "100%", height: 1, backgroundColor: "gray" }} />
</TouchableOpacity>
);
};
render() {
return (
<View style={{ flex: 1, padding: 48 }}>
<FlatList style={{ flex: 1, backgroundColor: "#aaa", borderColor: "gray", borderWidth: 1 }}
renderItem={this.renderItem}
data={this.state.items}
keyExtractor={item => `${item}`}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.refresh}
/>
}
/>
</View>
);
}
}
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 20
- Comments: 50 (6 by maintainers)
Links to this issue
Commits related to this issue
- 代码有执行,但是刷新效果出不来,https://github.com/facebook/react-native/issues/20011 — committed to Clearives/ccM by Clearives 6 years ago
- fix: First press not working after pull to refresh (#30291) Summary: According to https://github.com/facebook/react-native/issues/20011, the first onPress will not work after pull to refresh. Dive i... — committed to facebook/react-native by rnike 4 years ago
Same issue!
@ravirajn22 , thanks! Passing
disableScrollViewPanResponder
prop to myFlatList
fixed the bug.The following are the details I found by debugging, may be some one more familiar with reactnative event plugin could take it further.
The issue happens because
responderInst
is still kept hold by the ScrollView after all the events are fired whereas in case whereitems: []
is commented in the setState, theresponderInst
is correctly set to null.responderInst
is a react component which will get all the touch events, how it works and set can be found insetResponderAndExtractTransfer
in ReactNativeRenderer-dev.js.targetInst
is the react component on which the original touch happened.Nesting of components is like this View -> ScrollView -> (View -> Text ) * multiplied by number of list items
There are lots of events fired in following order
The partial flow is when we pull to refresh,
topTouchStart
event is fired which calls ScrollView’sscrollResponderHandleTouchStart
which setsisTouching
totrue
.isTouching
determines if the ScrollView wants to becomeresponderInst
whentopScroll
event is firedisTouching
is set tofalse
insidescrollResponderHandleTouchEnd
whenonTouchEnd
event is fired . In our case this function is never called. Because ReactNativeBridgeEventPlugin’s extractEvents which determines which listeners (on the component) to call depends ontargetInst
. Since we set items=[] in setState thetargetInst
becomes null and none of our listeners in ScrollVIew (ScrollView is the parent of items, since items is null we cannot know its parents now) are called after the items are cleared. Hence whenonTouchEnd
is firedscrollResponderHandleTouchEnd
of ScrollView is not called.Hope someone familiar with ScrollView responder system and react event system can take it further.
EDIT 1: In ScrollView setting
disableScrollViewPanResponder=true
will prevent this bug from happening, since it will prevent the ScrollView to become responder. But don’t use this, since I don’t know what regression it creates. Only purpose I added is for documenting.EDIT 2: Tagging people who might know about this, @shergin
@Rahulnagarwal perfect!
import { TouchableOpacity } from 'react-native-gesture-handler';
it worked here for meI’ve been stuck with a similar issue I don’t know if its related or not. I’m developing for android only using a ScrollView and on a real device I can click buttons, they respond to being touched with the downstate etc but onPress doesn’t trigger. disableScrollViewPanResponder = {true} did not help in this case
I recognized that not only
FlatList
item, but every singleTouchable
on the screen won’t callonPress
afterFlatList
refreshed.I used a setTimeout to solve the issue
<RefreshControl refreshing={this.state.refresh} onRefresh={() => setTimeout(() => { this.refeshAction() }, 200) } title="Test />
Same issue. Also setTimeout doesn’t work for me because of the breaking animation. Somebody has a better solution?
Same issue just push this inside the flatList
onRefresh={() => setTimeout(() => { this.refeshAction() }, 200) }
is working for me !!try import { TouchableOpacity } from ‘react-native-gesture-handler’; It’s working properly
Same issue. Every touchable item on screen is not clickable after refresh
I 'm having this issue without the refresh on version 0.59.6
Any workarounds or news on a fix?
This reproes with ScrollView, not just FlatList.
setInterval
in the componentDidMount which calls refresh(), that doesn’t repro.Someone will need to dig into the ScrollView implementation and see if something funky is happening with the refresh control. It could also be somewhere in the ScrollableMixin or something like that.
This is the example I used:
It wasn’t me at all, it was all @yum650350. This was their first contribution to React Native!
same bug here with the simple FlatList “react-native”: “0.62.2”
<ScrollView disableScrollViewPanResponder={true} refreshControl={ <RefreshControl refreshing={this.state.refreshing} onRefresh={this.onRefresh.bind(this)} /> } >
This worked for me
Thanks
This worked for me properly on a SectionList
Thanks a lot!
me too ,0.61.4
I don’t understand why this hasn’t been fixed yet. It only happens on Android
I’m really struggling with that. I don’t use
Refreshcontrol
but rather remove an item from the array.Here my setup
Here the process
FlatList
(data={data}
)renderItem
is wrapped in aTouchableOpacity
renderItem
an ID gets stored in the hookFlatList
re-renders and removes the itemI tried
import { TouchableOpacity } from 'react-native-gesture-handler'
as @souzaluiz suggested but it has no effect.Please help !!!
disableScrollViewPanResponder = {true} fix the bug
The workaround
setTimeout with 200ms delay
works for me, but the refresh indicator has some animation problem.