From a9e6759bb5d8f08c444b7a324c4488b9e5f2ed1b Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Thu, 14 Mar 2024 20:00:56 -0700 Subject: [PATCH] Fix maintainVisibleContentPosition on Android during momentum scroll (#43425) Summary: When using maintainVisibleContentPosition (mvcp) on Android it doesn't work properly when items are added during a momentum scroll. This happens because the android scrollview momentum scroll animation overrides the scroll position that the mvcp implementation sets [here](https://github.com/facebook/react-native/blob/2d547a3252b328251e49dabfeec85f8d46c85411/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/scroll/MaintainVisibleScrollPositionHelper.java#L132). To fix this we need to cancel the momentum scroll animation, update the scroll position then restart the scroll animation with the previous animation remaining momentum. ## Changelog: [ANDROID] [FIXED] - Fix maintainVisibleContentPosition during momentum scroll Pull Request resolved: https://github.com/facebook/react-native/pull/43425 Test Plan: Tested in RNTester on Android with both vertical and horizontal scrollviews using the following code: ```ts // packages/rn-tester/js/RNTesterAppShared.js import { Button, SafeAreaView, ScrollView, Text, View, } from 'react-native'; import React, {useLayoutEffect, useRef, useState} from 'react'; const generateUniqueKey = () => `_${Math.random().toString(36).substr(2, 9)}` const initialData = Array.from(Array(100).keys()).map(n => ({ id: generateUniqueKey(), value: n, })) function ListItem({item}) { const color = `hsl(${item.value * 10}, 75%, 85%)`; return ( List item: {item.value} ); } export default function FlatListRepro() { const numToAdd = 10; const [numbers, setNumbers] = useState(initialData); const ref = useRef(); const addAbove = () => { setNumbers(prev => { const additionalNumbers = Array.from(Array(numToAdd).keys()) .map(n => ({ id: generateUniqueKey(), value: prev[0].value - n - 1, })) .reverse(); return additionalNumbers.concat(prev); }); }; useLayoutEffect(() => { ref.current.scrollTo({y: numbers.length * 100, animated: false}); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return (