Skip to content

Commit

Permalink
Fix web useScrollViewOffset on wrapped scrolls (#5861)
Browse files Browse the repository at this point in the history
## Summary

Inspired by
#5790,
it seems like scrolls can be wrapped in other views (by for example
setting their `onRefresh` property), which makes refs to them point to
the wrapping view, not them. Thus I add support for such situations to
the `useScrollViewOffset` hook, which is our only hook that needs to
access the component itself.

## Test plan

<details>
<summary>
Testing code
</summary>

```tsx
import React from 'react';
import {
  Text,
  Pressable,
  SafeAreaView,
  StyleSheet,
  Button,
  findNodeHandle,
} from 'react-native';
import Animated, {
  useAnimatedRef,
  useScrollViewOffset,
} from 'react-native-reanimated';

export default function App() {
  const animatedRef = useAnimatedRef<Animated.ScrollView>();
  const offset = useScrollViewOffset(animatedRef);
  const [refreshing, setRefreshing] = React.useState(false);

  function printOffset() {
    console.log('OFFSET VALUE');
    console.log(offset.value);
  }

  return (
    <SafeAreaView>
      <Button
        title="refresh"
        onPress={() => setRefreshing((oldState) => !oldState)}
      />
      <List
        animatedRef={animatedRef}
        printOffset={printOffset}
        refreshing={refreshing}
      />
    </SafeAreaView>
  );
}

const List = ({ animatedRef, printOffset, refreshing }) => {
  console.log('tag', findNodeHandle(animatedRef.current));
  return (
    <Animated.FlatList
      ref={animatedRef}
      refreshing={refreshing}
      style={{height: 600}}
      onRefresh={() => console.log('refreshing')}
      onViewableItemsChanged={() => {
        printOffset();
      }}
      data={[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]}
      renderItem={({ _, index }) => <Item onPress={printOffset} id={index} />}
    />
  );
};

const Item = ({ onPress, id }) => {
  return (
    <Pressable onPress={onPress} style={styles.pressable}>
      <Text>{id}</Text>
    </Pressable>
  );
};

const styles = StyleSheet.create({
  pressable: {
    padding: 40,
    justifyContent: 'center',
    alignItems: 'center',
    borderWidth: 1,
    borderColor: 'red',
    backgroundColor: 'white',
  },
});
```
</details>
  • Loading branch information
szydlovsky authored Apr 3, 2024
1 parent 380c9cf commit 6dc1e6f
Showing 1 changed file with 20 additions and 11 deletions.
31 changes: 20 additions & 11 deletions src/reanimated2/hook/useScrollViewOffset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function useScrollViewOffsetWeb(

const eventHandler = useCallback(() => {
'worklet';
const element = animatedRef.current as unknown as HTMLElement;
const element = getWebScrollableElement(animatedRef.current);
// scrollLeft is the X axis scrolled offset, works properly also with RTL layout
offset.value =
element.scrollLeft === 0 ? element.scrollTop : element.scrollLeft;
Expand All @@ -45,14 +45,14 @@ function useScrollViewOffsetWeb(
useEffect(() => {
// We need to make sure that listener for old animatedRef value is removed
if (scrollRef.current !== null) {
(scrollRef.current as unknown as HTMLElement).removeEventListener(
getWebScrollableElement(scrollRef.current).removeEventListener(
'scroll',
eventHandler
);
}
scrollRef.current = animatedRef.current;

const element = animatedRef.current as unknown as HTMLElement;
const element = getWebScrollableElement(animatedRef.current);
element.addEventListener('scroll', eventHandler);
return () => {
element.removeEventListener('scroll', eventHandler);
Expand All @@ -66,14 +66,6 @@ function useScrollViewOffsetWeb(
return offset;
}

const scrollNativeEventNames = [
'onScroll',
'onScrollBeginDrag',
'onScrollEndDrag',
'onMomentumScrollBegin',
'onMomentumScrollEnd',
];

function useScrollViewOffsetNative(
animatedRef: AnimatedRef<AnimatedScrollView>,
providedOffset?: SharedValue<number>
Expand Down Expand Up @@ -115,3 +107,20 @@ function useScrollViewOffsetNative(

return offset;
}

function getWebScrollableElement(
scrollComponent: AnimatedScrollView | null
): HTMLElement {
return (
(scrollComponent?.getScrollableNode() as unknown as HTMLElement) ??
scrollComponent
);
}

const scrollNativeEventNames = [
'onScroll',
'onScrollBeginDrag',
'onScrollEndDrag',
'onMomentumScrollBegin',
'onMomentumScrollEnd',
];

0 comments on commit 6dc1e6f

Please sign in to comment.