Skip to content

Commit

Permalink
Don't call hooks conditionally in useScrollViewOffset (#5839)
Browse files Browse the repository at this point in the history
## Summary

It's bad for React, and we can handle creating an unused shared value
from time to time.

## Test plan

:shipit: 

<details>
<summary>
Testing snippet
</summary>

```tsx
import { StyleSheet, View } from 'react-native';

import Animated, {
  useSharedValue,
  useScrollViewOffset,
  useAnimatedRef,
  useAnimatedStyle,
} from 'react-native-reanimated';

import React from 'react';

export default function EmptyExample() {
  const scrollViewRefA = useAnimatedRef<Animated.ScrollView>();
  const scrollViewRefB = useAnimatedRef<Animated.ScrollView>();
  const myOffset = useSharedValue(0);
  const hookOffset = useScrollViewOffset(scrollViewRefA);
  const hookOffsetFromMyOffset = useScrollViewOffset(scrollViewRefB, myOffset);

  const animatedStyleA = useAnimatedStyle(() => ({
    borderRadius: 100 * Math.abs(Math.sin(hookOffset.value / 400)),
  }));

  const animatedStyleB = useAnimatedStyle(() => ({
    borderRadius: 100 * Math.abs(Math.sin(hookOffsetFromMyOffset.value / 400)),
  }));

  return (
    <View style={styles.container}>
      <Animated.ScrollView
        ref={scrollViewRefA}
        horizontal={true}
        style={styles.scrollView}>
        {Array.from({ length: 100 }).map((_, index) => (
          <Animated.View key={index} style={[styles.box, animatedStyleA]} />
        ))}
      </Animated.ScrollView>
      <Animated.ScrollView
        ref={scrollViewRefB}
        horizontal={true}
        style={styles.scrollView}>
        {Array.from({ length: 100 }).map((_, index) => (
          <Animated.View key={index} style={[styles.box, animatedStyleB]} />
        ))}
      </Animated.ScrollView>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
  box: {
    width: 200,
    height: 200,
    backgroundColor: 'blue',
    margin: 30,
  },
  scrollView: {
    maxWidth: 800,
    maxHeight: 300,
  },
});

```

</details>
  • Loading branch information
tjzel authored Mar 27, 2024
1 parent 915ff35 commit 75f9398
Showing 1 changed file with 10 additions and 14 deletions.
24 changes: 10 additions & 14 deletions src/reanimated2/hook/useScrollViewOffset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,17 @@ export const useScrollViewOffset = IS_WEB

function useScrollViewOffsetWeb(
animatedRef: AnimatedRef<AnimatedScrollView>,
initialRef?: SharedValue<number>
providedOffset?: SharedValue<number>
): SharedValue<number> {
const offsetRef = useRef(
// eslint-disable-next-line react-hooks/rules-of-hooks
initialRef !== undefined ? initialRef : useSharedValue(0)
);
const internalOffset = useSharedValue(0);
const offset = useRef(providedOffset ?? internalOffset).current;
const scrollRef = useRef<AnimatedScrollView | null>(null);

const eventHandler = useCallback(() => {
'worklet';
const element = animatedRef.current as unknown as HTMLElement;
// scrollLeft is the X axis scrolled offset, works properly also with RTL layout
offsetRef.current.value =
offset.value =
element.scrollLeft === 0 ? element.scrollTop : element.scrollLeft;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [animatedRef, animatedRef.current]);
Expand All @@ -66,7 +64,7 @@ function useScrollViewOffsetWeb(
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [animatedRef, animatedRef.current, eventHandler]);

return offsetRef.current;
return offset;
}

const scrollNativeEventNames = [
Expand All @@ -79,18 +77,16 @@ const scrollNativeEventNames = [

function useScrollViewOffsetNative(
animatedRef: AnimatedRef<AnimatedScrollView>,
initialRef?: SharedValue<number>
providedOffset?: SharedValue<number>
): SharedValue<number> {
const offsetRef = useRef(
// eslint-disable-next-line react-hooks/rules-of-hooks
initialRef !== undefined ? initialRef : useSharedValue(0)
);
const internalOffset = useSharedValue(0);
const offset = useRef(providedOffset ?? internalOffset).current;
const scrollRef = useRef<AnimatedScrollView | null>(null);

const eventHandler = useEvent<RNNativeScrollEvent>(
(event: ReanimatedScrollEvent) => {
'worklet';
offsetRef.current.value =
offset.value =
event.contentOffset.x === 0
? event.contentOffset.y
: event.contentOffset.x;
Expand Down Expand Up @@ -119,5 +115,5 @@ function useScrollViewOffsetNative(
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [animatedRef, animatedRef.current, eventHandler]);

return offsetRef.current;
return offset;
}

0 comments on commit 75f9398

Please sign in to comment.