Skip to content

Commit

Permalink
fix: switching lazy tabs sometimes jumps scroll position on ios (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
andreialecu authored Feb 11, 2021
1 parent fa5915d commit 6c3e9fd
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 5 deletions.
11 changes: 10 additions & 1 deletion src/FlatList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FlatList as RNFlatList, FlatListProps } from 'react-native'

import { AnimatedFlatList, IS_IOS } from './helpers'
import {
useAfterMountEffect,
useChainCallback,
useCollapsibleStyle,
useScrollHandlerY,
Expand All @@ -29,7 +30,15 @@ function FlatListImpl<R>(
scrollYCurrent,
} = useTabsContext()
const ref = useSharedAnimatedRef<RNFlatList<unknown>>(passRef)
const scrollHandler = useScrollHandlerY(name)
const [canBindScrollEvent, setCanBindScrollEvent] = React.useState(false)

useAfterMountEffect(() => {
// we enable the scroll event after mounting
// otherwise we get an `onScroll` call with the initial scroll position which can break things
setCanBindScrollEvent(true)
})

const scrollHandler = useScrollHandlerY(name, { enabled: canBindScrollEvent })
const {
style: _style,
contentContainerStyle: _contentContainerStyle,
Expand Down
13 changes: 12 additions & 1 deletion src/ScrollView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Animated from 'react-native-reanimated'

import { IS_IOS } from './helpers'
import {
useAfterMountEffect,
useChainCallback,
useCollapsibleStyle,
useScrollHandlerY,
Expand Down Expand Up @@ -32,11 +33,21 @@ export const ScrollView = React.forwardRef<
contentInset,
scrollYCurrent,
} = useTabsContext()
const scrollHandler = useScrollHandlerY(name)
const {
style: _style,
contentContainerStyle: _contentContainerStyle,
} = useCollapsibleStyle()
const [canBindScrollEvent, setCanBindScrollEvent] = React.useState(false)

useAfterMountEffect(() => {
// we enable the scroll event after mounting
// otherwise we get an `onScroll` call with the initial scroll position which can break things
setCanBindScrollEvent(true)
})

const scrollHandler = useScrollHandlerY(name, {
enabled: canBindScrollEvent,
})

React.useEffect(() => {
setRef(name, ref)
Expand Down
37 changes: 34 additions & 3 deletions src/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,10 @@ export function useScroller<T extends RefComponent>() {
return scroller
}

export const useScrollHandlerY = (name: TabName) => {
export const useScrollHandlerY = (
name: TabName,
{ enabled }: { enabled: boolean }
) => {
const {
accDiffClamp,
focusedTab,
Expand Down Expand Up @@ -253,6 +256,8 @@ export const useScrollHandlerY = (name: TabName) => {

const onMomentumEnd = () => {
'worklet'
if (!enabled) return

if (isDragging.value) return

if (typeof snapThreshold === 'number') {
Expand Down Expand Up @@ -327,6 +332,8 @@ export const useScrollHandlerY = (name: TabName) => {
const scrollHandler = useAnimatedScrollHandler(
{
onScroll: (event) => {
if (!enabled) return

if (focusedTab.value === name) {
if (IS_IOS) {
let { y } = event.contentOffset
Expand Down Expand Up @@ -380,6 +387,8 @@ export const useScrollHandlerY = (name: TabName) => {
}
},
onBeginDrag: () => {
if (!enabled) return

// workaround to stop animated scrolls
scrollTo(
refMap[name],
Expand All @@ -398,6 +407,8 @@ export const useScrollHandlerY = (name: TabName) => {
if (IS_IOS) cancelAnimation(afterDrag)
},
onEndDrag: () => {
if (!enabled) return

isGliding.value = true
isDragging.value = false

Expand All @@ -418,6 +429,8 @@ export const useScrollHandlerY = (name: TabName) => {
}
},
onMomentumBegin: () => {
if (!enabled) return

if (IS_IOS) {
cancelAnimation(afterDrag)
}
Expand All @@ -432,6 +445,7 @@ export const useScrollHandlerY = (name: TabName) => {
contentHeights,
snapThreshold,
clampMax,
enabled,
]
)

Expand All @@ -442,7 +456,8 @@ export const useScrollHandlerY = (name: TabName) => {
!isSnapping.value &&
!isScrolling.value &&
!isDragging.value &&
!isGliding.value
!isGliding.value &&
enabled
)
},
(sync) => {
Expand Down Expand Up @@ -479,7 +494,7 @@ export const useScrollHandlerY = (name: TabName) => {
}
}
},
[revealHeaderOnScroll, refMap, snapThreshold, tabIndex]
[revealHeaderOnScroll, refMap, snapThreshold, tabIndex, enabled]
)

return scrollHandler
Expand Down Expand Up @@ -515,3 +530,19 @@ export function useSharedAnimatedRef<T extends RefComponent>(

return ref
}

export function useAfterMountEffect(effect: React.EffectCallback) {
const [didExecute, setDidExecute] = useState(false)
const result = useEffect(() => {
if (didExecute) return

const timeout = setTimeout(() => {
effect()
setDidExecute(true)
}, 0)
return () => {
clearTimeout(timeout)
}
}, [didExecute, effect])
return result
}

0 comments on commit 6c3e9fd

Please sign in to comment.