software-mansion / react-native-reanimated

React Native's Animated library reimplemented
https://docs.swmansion.com/react-native-reanimated/
MIT License
8.82k stars 1.29k forks source link

[bug][iOS][Android]: FlatList (vertical) + ScrollView (horizontal inside renderItem) with useAnimatedScrollHandler() runs exactly "windowSize" times #2821

Closed hirbod closed 3 months ago

hirbod commented 2 years ago

Description

I have an infinite vertical FlatList with paging enabled. It's something like TikTok / Instagram Reels. I spent a ton of time to optimize the FlatList and managed to swipe through 25.000 videos without an crash/framedrop.

My renderItem method is very slim and just contains a horizontal scrollview with 2 views (paging enabled as well) The first View shows my video and the second horizontal view shows information about the video. In order to pause my video and to hide/show my bottom tab bar, I am using an Animated Scrollview with useAnimatedScrollHandler

Everything works perfectly fine, but as soon as FlatList's value of windowSize was swiped (in my case, 9), the useAnimatedScrollHandler is not triggering anymore.

  const scrollHandler = useAnimatedScrollHandler(
    {
      onScroll: (event) => {
        if (!focused) return;

        if (lastContentOffset.value > event.contentOffset.x) {
          if (isScrolling.value) {
            runOnJS(showTabBar)();
          }
        } else if (lastContentOffset.value < event.contentOffset.x) {
          if (isScrolling.value) {
            runOnJS(hideTabBar)();
          }
        }
        lastContentOffset.value = event.contentOffset.x;
      },
      onBeginDrag: (e) => {
        isScrolling.value = true;
      },
      onEndDrag: (e) => {
        isScrolling.value = false;
      }
    },
    [focused]
  );
  const renderItem = useCallback(
    (item) => (
      <Reanimated.ScrollView
        horizontal
        alwaysBounceVertical={false}
        alwaysBounceHorizontal={false}
        bounces={false}
        showsHorizontalScrollIndicator={false}
        onScroll={scrollHandler}
        overScrollMode="never"
        snapToInterval={videoHeight}
        disableIntervalMomentum={true}
        decelerationRate={'fast'}
        scrollsToTop={false}
        scrollEventThrottle={64}
        onMomentumScrollEnd={onScrollEnd}
        directionalLockEnabled
      >
        <View style={{ width: width }}>
          <SingleVideo
            ref={(ref) => {
              cellRefs.current[item.index] = ref;
            }}
          />
        </View>
        <View>
                <VideoMeta />
        </View>
      </Reanimated.ScrollView>
    ),
    []
  );
    <View style={{ flexGrow: 1 }}>
      <FlatList
        ref={flatlistRef}
        data={items}
        renderItem={renderItem}
        keyExtractor={keyExtractor}
        initialNumToRender={1}
        maxToRenderPerBatch={6}
        updateCellsBatchingPeriod={20}
        windowSize={9}
        getItemLayout={_itemLayout}
        viewabilityConfig={viewabilityConfig}
        removeClippedSubviews={true}
        onViewableItemsChanged={onViewableItemsChanged}
        snapToAlignment="start"
        snapToInterval={videoHeight}
        disableIntervalMomentum={true}
        decelerationRate={'fast'}
        scrollsToTop={false}
        showsVerticalScrollIndicator={false}
        pagingEnabled
      />
    </View>

Expected behavior

I should be able to create as many ScrollView with as many onScroll Event Listeners inside a FlatList as I want to.

Actual behavior & steps to reproduce

As soon as windowSize elements have passed, onScroll won't be triggered anymore.

Snack or minimal code example

Package versions

name version
react-native 0.64.2
react-native-reanimated 2.3.1
expo SDK 44 (expo-dev-client)

Affected platforms

github-actions[bot] commented 2 years ago

Hey! 👋

It looks like you've omitted a few important sections from the issue template.

Please complete Snack or minimal code example section.

hirbod commented 2 years ago

Just confirmed that the same issue happens on Android

hirbod commented 2 years ago

I removed useAnimatedScrollHandler and used all onboard scrollview events and it works flawlessly. (onScroll, onScrollBeginDrag, onScrollEndDrag=

kmagiera commented 2 years ago

hi @Hirbod – thanks for reporting. Apparently, I'm not sure if I fully understand the issue.

What you mean by "windowSize elements have passed" ? There is a number of small code snippets in the description but what I think would clarify the issue for me if we just get a complete example or component that we could try.