Skip to content

Commit

Permalink
Avoid re-rendering from useIsFocused
Browse files Browse the repository at this point in the history
  • Loading branch information
samholmes committed Jan 15, 2025
1 parent 522d485 commit 3329be6
Showing 1 changed file with 33 additions and 17 deletions.
50 changes: 33 additions & 17 deletions src/state/SceneScrollState.tsx
Original file line number Diff line number Diff line change
@@ -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'

Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand All @@ -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 })
}
}
)
Expand Down

0 comments on commit 3329be6

Please sign in to comment.