Skip to content

Commit 690b887

Browse files
authored
Fix scrollTo on FlashList (software-mansion#4384)
## Summary Fixes software-mansion#4376. Analogous to software-mansion#3988. Solution: copy the code from `createAnimatedComponent.tsx` to `useAnimatedRef.ts` https://github.com/software-mansion/react-native-reanimated/blob/87fb9c6a1fb6b8c839b65b90ad16f574f45a1294/src/createAnimatedComponent.tsx#L289-L291 Example: <details> <summary>App.tsx</summary> ```tsx import Animated, { runOnUI, scrollTo, useAnimatedRef, } from 'react-native-reanimated'; import {Button, StyleSheet, Switch, Text, View} from 'react-native'; import {FlashList} from '@shopify/flash-list'; import React from 'react'; declare const _WORKLET: boolean; const AnimatedFlashList = Animated.createAnimatedComponent(FlashList); interface Item { label: string; } const DATA: Item[] = [...Array(100)].map((_, i) => ({label: String(i)})); function getRandomOffset() { 'worklet'; return Math.random() * 2000; } export default function ScrollToExample() { const [animated, setAnimated] = React.useState(true); const aref = useAnimatedRef<FlashList<Item>>(); const scrollFromJS = () => { console.log(_WORKLET); aref.current?.scrollToOffset({offset: getRandomOffset(), animated}); }; const scrollFromUI = () => { runOnUI(() => { 'worklet'; console.log(_WORKLET); scrollTo(aref, 0, getRandomOffset(), animated); })(); }; return ( <> <View style={styles.buttons}> <Switch value={animated} onValueChange={setAnimated} style={styles.switch} /> <Button onPress={scrollFromJS} title="Scroll from JS" /> <Button onPress={scrollFromUI} title="Scroll from UI" /> </View> <AnimatedFlashList // @ts-ignore createAnimatedComponent seems to break generic types ref={aref} data={DATA} // @ts-ignore createAnimatedComponent seems to break generic types renderItem={({item}: {item: Item}) => ( <Text key={item.label} style={styles.text}> {item.label} </Text> )} estimatedItemSize={200} /> </> ); } const styles = StyleSheet.create({ switch: { marginBottom: 10, }, buttons: { marginTop: 80, marginBottom: 40, alignItems: 'center', }, text: { fontSize: 50, textAlign: 'center', }, }); ``` </details> Before: <img src="https://user-images.githubusercontent.com/20516055/233323545-427cc3d4-4479-4b02-a24b-5a3039fb3105.png" width="300" /> Android: https://user-images.githubusercontent.com/20516055/233322746-637fa2a9-0e93-40ac-baa8-c3c3d2449867.mp4 iOS: https://user-images.githubusercontent.com/20516055/233322714-20c03ade-1f15-4d76-8a5e-b4b59ebe8a73.mp4 ## Test plan 1. Install FlashList in Example app with `yarn add @shopify/flash-list && cd ios && pod install` 2. Paste the attached code snippet into App.tsx > **Warning** > It looks like animated refs don't update properly on render. In this case, pressing "Scroll from UI" button after a fast refresh does nothing. Please reload the <kbd>r</kbd> to reload the app and check again.
1 parent 1619619 commit 690b887

1 file changed

Lines changed: 11 additions & 3 deletions

File tree

src/reanimated2/hook/useAnimatedRef.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ import {
99
registerShareableMapping,
1010
} from '../shareables';
1111

12+
interface ComponentRef extends Component {
13+
getScrollableNode?: () => ComponentRef;
14+
}
15+
1216
function getShareableShadowNodeFromComponent(
13-
component: Component
17+
component: ComponentRef
1418
): ShadowNodeWrapper {
1519
return getShadowNodeWrapperFromHostInstance(component);
1620
}
@@ -19,15 +23,19 @@ const getTagValueFunction = global._IS_FABRIC
1923
? getShareableShadowNodeFromComponent
2024
: getTag;
2125

22-
export function useAnimatedRef<T extends Component>(): RefObjectFunction<T> {
26+
export function useAnimatedRef<T extends ComponentRef>(): RefObjectFunction<T> {
2327
const tag = useSharedValue<number | ShadowNodeWrapper | null>(-1);
2428
const ref = useRef<RefObjectFunction<T>>();
2529

2630
if (!ref.current) {
2731
const fun: RefObjectFunction<T> = <RefObjectFunction<T>>((component) => {
2832
// enters when ref is set by attaching to a component
2933
if (component) {
30-
tag.value = getTagValueFunction(component);
34+
tag.value = getTagValueFunction(
35+
component.getScrollableNode
36+
? component.getScrollableNode()
37+
: component
38+
);
3139
fun.current = component;
3240
}
3341
return tag.value;

0 commit comments

Comments
 (0)