Skip to content

VirtualizedList - Viewability regression, seems affected by web implementation of Animated #2574

Open
@mtourj

Description

@mtourj

Is there an existing issue for this?

  • I have searched the existing issues

Edit: #2566 may be related

Describe the issue

Hello, I recently upgraded from 18.11 to 19.6, and I noticed that some of my components that use FlatList and rely on viewabilityConfig were no longer working properly. It seems onViewableItemsChanged stops working in some very specific cases. In the repro I provide, viewability breaks when I run Animated.loop on an animation that has useNativeDriver set to false, and after changing state on the data being rendered by the FlatList.

However, after fixing the issue by setting useNativeDriver: true on the animation that was causing it, I found that other areas of my app where also causing viewability to break even though I had no other instances of Animated.loop being called, so clearly this is not the only case that can cause this viewability issue to occur.

This is important for us because we rely on viewabilityConfig to determine when videos in a flatlist should stop/start playing.

Thanks for the great work bringing react native to the web!

Expected behavior

For onViewableItemsChanged to be called as expected, regardless of whether an animation is running in another part of the app

Steps to reproduce

  1. Create FlatList with viewability config props passed in:
<FlatList
          data={data}
          horizontal
          renderItem={({ item, index: idx }) =>
            renderItem({
              item,
              index,
              isViewable: viewableItemIndices.includes(idx),
            })
          }
          viewabilityConfigCallbackPairs={
            viewabilityConfigCallbackPairs.current
          }
        />

with the following values for viewabilityConfigCallbackPairs:

const VIEWABILITY_CONFIG = {
  viewAreaCoveragePercentThreshold: 50,
};

function MyComponent() {
...
...
  const [viewableItemIndices, setViewableItemIndices] = useState<number[]>([]);

  const viewabilityConfigCallbackPairs = useRef([
    {
      viewabilityConfig: VIEWABILITY_CONFIG,
      onViewableItemsChanged: (info: {
        viewableItems: ViewToken[];
        changed: ViewToken[];
      }) => {
        console.log(
          'called',
          info.viewableItems.map((item) => item.index as number),
        );
        setViewableItemIndices(
          info.viewableItems.map((item) => item.index as number) || [],
        );
      },
    },
  ]);
  ...
  1. Start an animation loop with useNativeDriver: false
useEffect(() => {
  animationLoop.current = Animated.timing(animationRef.current, {
    toValue: 2,
    duration: 1500,
    useNativeDriver: false
  });
  animationRef.current.setValue(0);
  Animated.loop(animationLoop.current).start();
}, []);
  1. Make a state change on the data being rendered by FlatList, changing the length of the array:
setData([...])
  1. Note how the console.log() call in onViewableItemsChanged is never reached

Test case

https://codesandbox.io/s/weird-virtualizedlist-2rc2ln?file=/src/App.js

Additional comments

I am not familiar with React Native internals, so I just hope this repro can point you to the root cause and help solve this problematic issue. Thank you!!

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugbug: react-nativeBug associated with upstream React Native vendor code

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions