From 3329be6e25411cdb06c97d3288d603e95199b4e4 Mon Sep 17 00:00:00 2001 From: Sam Holmes Date: Thu, 2 Jan 2025 15:19:00 -0800 Subject: [PATCH] Avoid re-rendering from useIsFocused --- src/state/SceneScrollState.tsx | 50 ++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/state/SceneScrollState.tsx b/src/state/SceneScrollState.tsx index adb2b7109de..6230fef3e8a 100644 --- a/src/state/SceneScrollState.tsx +++ b/src/state/SceneScrollState.tsx @@ -1,7 +1,7 @@ -import { useIsFocused } from '@react-navigation/native' -import { useMemo } from 'react' +import { useFocusEffect } from '@react-navigation/native' +import { useCallback, useMemo } from 'react' import { NativeScrollEvent, NativeSyntheticEvent } from 'react-native' -import { SharedValue, useAnimatedReaction, useAnimatedScrollHandler, useSharedValue, useWorkletCallback } from 'react-native-reanimated' +import { SharedValue, useAnimatedReaction, useAnimatedScrollHandler, useSharedValue, useWorkletCallback, withTiming } from 'react-native-reanimated' import { createStateProvider } from './createStateProvider' @@ -51,20 +51,28 @@ export const useSceneScrollHandler = (): SceneScrollHandler => { const scrollMomentumEndEvent = useSceneScrollContext(state => state.scrollMomentumEndEvent) const scrollY = useSceneScrollContext(state => state.scrollY) + const isFocused = useSharedValue(false) + useFocusEffect( + useCallback(() => { + isFocused.value = true + return () => { + isFocused.value = false + } + }, [isFocused]) + ) + // This fixes a bug during scene transition where the scene that is being // left was the last to update the scrollY value. - const isFocused = useIsFocused() const localScrollY = useSharedValue(0) useAnimatedReaction( () => { - return isFocused + return isFocused.value }, isFocused => { if (isFocused && localScrollY.value !== scrollY.value) { - scrollY.value = localScrollY.value + scrollY.value = withTiming(localScrollY.value, { duration: 300 }) } - }, - [isFocused] + } ) // In each handler, we check `isFocused` to avoid mutating state if the @@ -73,39 +81,39 @@ export const useSceneScrollHandler = (): SceneScrollHandler => { onScroll: (nativeEvent: NativeScrollEvent) => { 'worklet' // Avoids unexpected triggers - if (!isFocused) return + if (!isFocused.value) return // Condition avoids thrashing if (scrollY.value !== nativeEvent.contentOffset.y) { localScrollY.value = nativeEvent.contentOffset.y - scrollY.value = localScrollY.value + scrollY.value = nativeEvent.contentOffset.y } }, onBeginDrag: (nativeEvent: NativeScrollEvent) => { 'worklet' // Avoids unexpected triggers - if (!isFocused) return + if (!isFocused.value) return scrollBeginEvent.value = nativeEvent }, onEndDrag: nativeEvent => { 'worklet' // Avoids unexpected triggers - if (!isFocused) return + if (!isFocused.value) return scrollEndEvent.value = nativeEvent }, onMomentumBegin: nativeEvent => { 'worklet' // Avoids unexpected triggers - if (!isFocused) return + if (!isFocused.value) return scrollMomentumBeginEvent.value = nativeEvent }, onMomentumEnd: nativeEvent => { 'worklet' // Avoids unexpected triggers - if (!isFocused) return + if (!isFocused.value) return scrollMomentumEndEvent.value = nativeEvent } @@ -120,17 +128,25 @@ export const useSceneScrollWorkletHandler = () => { const scrollY = useSceneScrollContext(state => state.scrollY) // Create shared values for scroll position - const isFocused = useIsFocused() + const isFocused = useSharedValue(false) + useFocusEffect( + useCallback(() => { + isFocused.value = true + return () => { + isFocused.value = false + } + }, [isFocused]) + ) const localScrollY = useSharedValue(0) useAnimatedReaction( () => { - return isFocused + return isFocused.value }, isFocusedResult => { if (isFocusedResult && localScrollY.value !== scrollY.value) { - scrollY.value = localScrollY.value + scrollY.value = withTiming(localScrollY.value, { duration: 300 }) } } )