Skip to content

Commit

Permalink
Merge pull request #394 from PedroBern/fix-conditionallazypanesync
Browse files Browse the repository at this point in the history
fix: dynamic tab sync
  • Loading branch information
andreialecu committed Apr 5, 2024
2 parents 0ab411d + 75c6e3a commit e5d3455
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 68 deletions.
3 changes: 1 addition & 2 deletions example/src/ConditionalTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useState } from 'react'
import { View, Text, StyleSheet } from 'react-native'
import { TouchableOpacity } from 'react-native-gesture-handler'
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'

import ExampleComponent from './Shared/ExampleComponent'
import { ExampleComponentType } from './types'
Expand Down
7 changes: 4 additions & 3 deletions example/src/DynamicTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react'
import { Text, View, StyleSheet } from 'react-native'
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native'
import * as Tabs from 'react-native-collapsible-tab-view'
import { TouchableOpacity } from 'react-native-gesture-handler'

import { AlbumsContent } from './Shared/Albums'
import { ArticleContent } from './Shared/Article'
Expand Down Expand Up @@ -75,7 +74,9 @@ const DynamicTabs: ExampleComponentType = () => {
}

const TabBarComponent = React.useCallback(
(props) => <Tabs.MaterialTabBar {...props} scrollEnabled />,
(props: Tabs.MaterialTabBarProps<string>) => (
<Tabs.MaterialTabBar {...props} scrollEnabled />
),
[]
)

Expand Down
23 changes: 13 additions & 10 deletions src/Container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const Container = React.memo(
const accDiffClamp: ContextType['accDiffClamp'] = useSharedValue(0)
const scrollYCurrent: ContextType['scrollYCurrent'] = useSharedValue(0)
const scrollY: ContextType['scrollY'] = useSharedValue(
tabNamesArray.map(() => 0)
Object.fromEntries(tabNamesArray.map((n) => [n, 0]))
)

const contentHeights: ContextType['contentHeights'] = useSharedValue(
Expand All @@ -137,12 +137,6 @@ export const Container = React.memo(
: 0
)

const [data, setData] = React.useState(tabNamesArray)

React.useEffect(() => {
setData(tabNamesArray)
}, [tabNamesArray])

const focusedTab: ContextType['focusedTab'] =
useDerivedValue<TabName>(() => {
return tabNames.value[index.value]
Expand Down Expand Up @@ -226,15 +220,22 @@ export const Container = React.memo(
(i) => {
if (i !== index.value) {
offset.value =
scrollY.value[index.value] - scrollY.value[i] + offset.value
scrollY.value[tabNames.value[index.value]] -
scrollY.value[tabNames.value[i]] +
offset.value
runOnJS(propagateTabChange)({
prevIndex: index.value,
index: i,
prevTabName: tabNames.value[index.value],
tabName: tabNames.value[i],
})
index.value = i
scrollYCurrent.value = scrollY.value[index.value] || 0
if (
typeof scrollY.value[tabNames.value[index.value]] === 'number'
) {
scrollYCurrent.value =
scrollY.value[tabNames.value[index.value]] || 0
}
}
},
[]
Expand Down Expand Up @@ -433,13 +434,15 @@ export const Container = React.memo(
{...pagerProps}
style={[pagerProps?.style, StyleSheet.absoluteFill]}
>
{data.map((tabName, i) => {
{tabNamesArray.map((tabName, i) => {
return (
<View key={i}>
<TabNameContext.Provider value={tabName}>
<Lazy
startMounted={lazy ? undefined : true}
cancelLazyFadeIn={!lazy ? true : !!cancelLazyFadeIn}
// ensure that we remount the tab if its name changes but the index doesn't
key={tabName}
>
{
React.Children.toArray(children)[
Expand Down
23 changes: 13 additions & 10 deletions src/Lazy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,17 @@ export const Lazy: React.FC<{
}
}, [])

const startMountTimer = React.useCallback(() => {
// wait the scene to be at least mountDelay ms focused, before mounting
setTimeout(() => {
if (focusedTab.value === name) {
if (isSelfMounted.current) setCanMount(true)
}
}, mountDelayMs)
}, [focusedTab.value, mountDelayMs, name])
const startMountTimer = React.useCallback(
(focusedTab: string) => {
// wait the scene to be at least mountDelay ms focused, before mounting
setTimeout(() => {
if (focusedTab === name) {
if (isSelfMounted.current) setCanMount(true)
}
}, mountDelayMs)
},
[mountDelayMs, name]
)

useAnimatedReaction(
() => {
Expand All @@ -87,7 +90,7 @@ export const Lazy: React.FC<{
opacity.value = 1
runOnJS(setCanMount)(true)
} else {
runOnJS(startMountTimer)()
runOnJS(startMountTimer)(focusedTab.value)
}
}
},
Expand Down Expand Up @@ -116,7 +119,7 @@ export const Lazy: React.FC<{
return {
opacity: opacity.value,
}
}, [])
}, [opacity])

const onLayout = useCallback(() => {
didTriggerLayout.value = true
Expand Down
48 changes: 10 additions & 38 deletions src/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,6 @@ export const useScrollHandlerY = (name: TabName) => {
revealHeaderOnScroll,
refMap,
tabNames,
index,
headerHeight,
contentInset,
containerHeight,
Expand All @@ -275,19 +274,13 @@ export const useScrollHandlerY = (name: TabName) => {
enabled.value = toggle

if (toggle) {
const tabIndex = tabNames.value.findIndex((n) => n === name)

const ref = refMap[name]
scrollTo(
ref,
0,
scrollY.value[tabIndex],
false,
`[${name}] restore scroll position - enable`
)
const y = scrollY.value[name] ?? scrollYCurrent.value

scrollTo(ref, 0, y, false, `[${name}] restore scroll position - enable`)
}
},
[enabled, name, refMap, scrollTo, scrollY.value, tabNames.value]
[enabled, name, refMap, scrollTo, scrollY.value, scrollYCurrent.value]
)

/**
Expand Down Expand Up @@ -399,9 +392,9 @@ export const useScrollHandlerY = (name: TabName) => {
scrollYCurrent.value = y
}

scrollY.value[index.value] = scrollYCurrent.value
scrollY.value[name] = scrollYCurrent.value
oldAccScrollY.value = accScrollY.value
accScrollY.value = scrollY.value[index.value] + offset.value
accScrollY.value = scrollY.value[name] + offset.value

if (revealHeaderOnScroll) {
const delta = accScrollY.value - oldAccScrollY.value
Expand Down Expand Up @@ -485,8 +478,8 @@ export const useScrollHandlerY = (name: TabName) => {
focusedTab.value !== name
) {
let nextPosition: number | null = null
const focusedScrollY = scrollY.value[Math.round(indexDecimal.value)]
const tabScrollY = scrollY.value[tabIndex]
const focusedScrollY = scrollY.value[focusedTab.value]
const tabScrollY = scrollY.value[name]
const areEqual = focusedScrollY === tabScrollY

if (!areEqual) {
Expand Down Expand Up @@ -514,7 +507,7 @@ export const useScrollHandlerY = (name: TabName) => {

if (nextPosition !== null) {
// console.log(`sync ${name} ${nextPosition}`)
scrollY.value[tabIndex] = nextPosition
scrollY.value[name] = nextPosition
scrollTo(refMap[name], 0, nextPosition, false, `[${name}] sync pane`)
}
}
Expand Down Expand Up @@ -560,38 +553,17 @@ export function useAfterMountEffect(
nextOnLayout: ViewProps['onLayout'],
effect: React.EffectCallback
) {
const name = useTabNameContext()
const {
//tabsMounted,
refMap,
scrollY,
//scrollYCurrent,
tabNames,
} = useTabsContext()

const didExecute = useRef(false)
const didMount = useSharedValue(false)

const scrollTo = useScroller()
const ref = name ? refMap[name] : null

useAnimatedReaction(
() => {
return didMount.value
},
(didMount, prevDidMount) => {
if (didMount && !prevDidMount) {
if (didExecute.current) return
if (ref) {
const tabIndex = tabNames.value.findIndex((n) => n === name)
scrollTo(
ref,
0,
scrollY.value[tabIndex],
false,
`[${name}] restore scroll position`
)
}

effect()
didExecute.current = true
}
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export type ContextType<T extends TabName = TabName> = {
/**
* Array of the scroll y position of each tab.
*/
scrollY: SharedValue<number[]>
scrollY: SharedValue<Record<string, number>>
containerHeight: SharedValue<number | undefined>
/**
* Object containing the ref of each scrollable component.
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6925,10 +6925,10 @@ human-signals@^5.0.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28"
integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==

husky@7:
version "7.0.4"
resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535"
integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==
husky@^9.0.11:
version "9.0.11"
resolved "https://registry.yarnpkg.com/husky/-/husky-9.0.11.tgz#fc91df4c756050de41b3e478b2158b87c1e79af9"
integrity sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==

iconv-lite@^0.4.24:
version "0.4.24"
Expand Down

0 comments on commit e5d3455

Please sign in to comment.