Open tjzel opened 1 year ago
Could you put your snippet in this repro template @tjzel and attach it to this issue? https://github.com/react-native-community/reproducer-react-native
Sure, it's added!
Just a sidenote, I’ve seen this happening with FlashList
too.
Hey folks, is this PR responsible for this?
After building from source with the above PR included it's even worse @ryancat:
@efstathiosntonas #38475 is meant to support props that allow ScrollView to get throttled. If not used it should have no effect on ScrollView. I'll take a look to see why it's making any differences here.
Thanks for the clarification @ryancat. Are there any updates on this one @cortinico?
Thanks for the clarification @ryancat. Are there any updates on this one @cortinico?
No updates. We're happy to receive a PR that fixes this (so it's up for grab)
@efstathiosntonas #38475 is meant to support props that allow ScrollView to get throttled. If not used it should have no effect on ScrollView. I'll take a look to see why it's making any differences here.
VirtualizedList is defaulting scrollEventThrottle
to 50 (See source), which is what FlatList using and got impacted.
I thought about dropping the default for Android, but on iOS we've been having the same default already. It's the right thing to do to align both platforms and to have a default so that we can improve native frame rate. If you feel the throttle is working against your use case, you may set a lower value (any value less than 17 should have no effect on throttling).
I think we should remove this default, even though it was previously set on iOS. It is going to be incorrect for the vast majority of use-cases. Users shouldn't need to know on an RN update that they now need to set an obscure prop to a lower value. Time-based throttling has other issues as well (and VirtualizedList already has a mechanism to avoid starvation).
any value less than 17 should have no effect on throttling
Separate from the throttling, we should not be adding new code to React Native which is hardcoded to assume 60fps. This is not correct on many devices, including even older or lower end Android devices.
Anecdotally, I am aware of some code which implements a similar scenario and it seems to work okay on Android. Their implementation looks like:
top
might cause relayout?)Animated.FlatList
and the Animated.event
APIs. Can natively accelerate the transforms without hitting JS I think onScroll={Animated.event(
[{nativeEvent: {contentOffset: {x: this._scrollViewPos}}}],
{useNativeDriver: true},
)}
Animated.FlatList
does override scrollEventThrottle
to effectively disable it, as well.
@NickGerleman I removed scrollEventThrottle
on VirtualizedLists
and it throws this:
"@react-native/virtualized-lists": "^0.72.6"
You specified `onScroll` on a <ScrollView> but not `scrollEventThrottle`. You will only receive one event. Using `16` you get all the events but be aware that it may cause frame drops, use a bigger number if you don't need as much precision.
@NickGerleman Yeah there are several workarounds for this and by no means my code is made to be efficient - while using translate
sounds like a smart move I think people actually want to use layout redefining prop in this scenario.
translateY
can be really tricky though since it “shifts” the entire list up leaving empty space at the bottom
@NickGerleman I removed
scrollEventThrottle
onVirtualizedLists
and it throws this:
"@react-native/virtualized-lists": "^0.72.6"
You specified `onScroll` on a <ScrollView> but not `scrollEventThrottle`. You will only receive one event. Using `16` you get all the events but be aware that it may cause frame drops, use a bigger number if you don't need as much precision.
I ended up running into this with the above PR. Some components like Animated.FlatList
currently pass scrollEventThrottle={0.0001}
as a value to cause iOS to emit scroll events, while not actually throttle, so I am changing the effective default to that to keep behavior of no throttling on Android, and to change before to not throttle on iOS by default.
This specific behavior and expectation is a bit quirky, and is probably some artifact of RN back in the day being written for iOS before Android, and the two diverging. I am kind of tempted to try to run an experiment to change this behavior in the new architecture (maybe not worth the churn to change behavior in Paper).
@NickGerleman thanks for support on this, we've tried using translateY
as suggested, but we still see the stuttering in Android, as well we see issues with the footer, as not being able to scroll the whole list. Do you see any workaround for this with current RN?
Using demo, modified to:
import React from 'react';
import {Animated, Dimensions, StyleSheet, Text, View} from 'react-native';
const {height: SCREEN_HEIGHT} = Dimensions.get('screen');
const data = Array.from({length: 60}, (_, index) => ({
id: `${index + 1}`,
text: `Item ${index + 1}`,
}));
const App = () => {
const scrollY = new Animated.Value(0);
const translateY = scrollY.interpolate({
inputRange: [0, SCREEN_HEIGHT],
outputRange: [SCREEN_HEIGHT * 0.7, 0],
extrapolate: 'extend',
});
const renderItem = ({item}) => (
<View style={styles.item}>
<Text>{item.text}</Text>
</View>
);
return (
<View style={styles.container}>
<View style={styles.containerBehind}>
<Text>Multiplier: 1.0</Text>
</View>
<Animated.FlatList
data={data}
renderItem={renderItem}
style={[
styles.listContainer,
{
transform: [{translateY}],
paddingTop: scrollY,
},
]}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: scrollY}}}],
{useNativeDriver: false},
)}
ListFooterComponent={<Animated.View style={{height: scrollY}} />}
scrollEventThrottle={16}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
containerBehind: {
width: '100%',
height: '100%',
flex: 1,
backgroundColor: 'red',
alignItems: 'center',
justifyContent: 'center',
},
listContainer: {
left: 0,
position: 'absolute',
zIndex: 10,
width: '100%',
height: '100%',
backgroundColor: 'green',
},
item: {
height: 50,
},
});
export default App;
Hey @NickGerleman, have you had the time to look into this? I built an app with nightly RN version and it seems the issue is still there.
It still persist. Is there any update @NickGerleman
I guess onScroll event has missing layout calculation, the list height is increase if another view height decrease which outside of the list(padding leads bug in this case). IOS side has no recalculation of the list height during scroll so there is no stuttering @tjzel @NickGerleman
Description
When you create a FlatList that moves as it's being scrolled (change in offset for the FlatList is a change in
top
property of FlatList's style) it stutters heavily. This behavior is much less disruptive when movement is scaled down compared to scrolling and becomes app-breaking when upscaled. See provided videos:It was originally an issue in react-native-reanimated but after digging into it I narrowed it down to
react-native
and rewrote the code to not use react-native-reanimated. It seems to had been in the code for a long time, since this posts that dates 3 years back is about the same thing.I checked it on versions 0.72, 0.71 and 0.70. On both Fabric and Paper and the result is always the same (although it feels a bit like Fabric behaviour is worse).
iOS works properly on each version too.
It seems like there is a problem when calculating layout and touch events - that's what I conducted due to
multiplier
making it better or worse. After moving a FlatList twice the distance it scrolled and having the component moved React Native seems to 'think' that the finger is now further than it should be and lowers the offset? That's just my guess.React Native Version
0.72.3
Output of
npx react-native info
System: OS: macOS 13.4.1 CPU: (10) arm64 Apple M2 Pro Memory: 49.31 MB / 16.00 GB Shell: version: "5.9" path: /bin/zsh Binaries: Node: version: 18.16.0 path: /var/folders/jg/m839qn593nn7w_h3n0r9k25c0000gn/T/yarn--1689593442476-0.9098900178237601/node Yarn: version: 1.22.19 path: /var/folders/jg/m839qn593nn7w_h3n0r9k25c0000gn/T/yarn--1689593442476-0.9098900178237601/yarn npm: version: 9.5.1 path: ~/.nvm/versions/node/v18.16.0/bin/npm Watchman: version: 2023.06.12.00 path: /opt/homebrew/bin/watchman Managers: CocoaPods: version: 1.12.1 path: /Users/user/.rbenv/shims/pod SDKs: iOS SDK: Platforms:
Steps to reproduce
Just need to run the provided code snippet.
Snack, code example, screenshot, or link to a repository
https://github.com/tjzel/ReactNativeMovingFlatList