Last active
January 21, 2025 14:01
-
-
Save intergalacticspacehighway/02a36b05b2236bc750a065833b71c94a to your computer and use it in GitHub Desktop.
Viewability tracker with shared values
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { createContext, forwardRef, useCallback, useMemo } from "react"; | |
import { FlatList, FlatListProps, ViewToken } from "react-native"; | |
import Animated, { useSharedValue } from "react-native-reanimated"; | |
const MAX_VIEWABLE_ITEMS = 4; | |
type ViewabilityItemsContextType = string[]; | |
export const ViewabilityItemsContext = createContext< | |
Animated.SharedValue<ViewabilityItemsContextType> | |
>({ | |
value: [], | |
}); | |
export const ViewabilityTrackerFlatlist = forwardRef( | |
(props: FlatListProps<any>, ref: any) => { | |
const visibleItems = useSharedValue<ViewabilityItemsContextType>([]); | |
const { keyExtractor, renderItem: _renderItem } = props; | |
const renderItem = useCallback( | |
(params: any) => ( | |
<ItemKeyContext.Provider | |
value={keyExtractor?.(params.item, params.index)} | |
> | |
{_renderItem?.(params)} | |
</ItemKeyContext.Provider> | |
), | |
[_renderItem, keyExtractor] | |
); | |
const onViewableItemsChanged = useCallback(({ viewableItems }: any) => { | |
visibleItems.value = viewableItems | |
.slice(0, MAX_VIEWABLE_ITEMS) | |
.map((item: any) => item.key); | |
}, []); | |
return ( | |
<ViewabilityItemsContext.Provider value={visibleItems}> | |
<FlatList | |
{...props} | |
onViewableItemsChanged={onViewableItemsChanged} | |
ref={ref} | |
viewabilityConfig={useMemo( | |
() => ({ | |
itemVisiblePercentThreshold: 50, | |
minimumViewTime: 100, | |
}), | |
[] | |
)} | |
renderItem={renderItem} | |
/> | |
</ViewabilityItemsContext.Provider> | |
); | |
} | |
); | |
export const ItemKeyContext = createContext<string | undefined>(undefined); | |
// Usage in item to do stuff. | |
export const useDoSomethingWhenItemVisible = () => { | |
const id = useContext(ItemKeyContext); | |
const context = useContext(ViewabilityItemsContext); | |
// we mount or unmount the Video depending on the list visibility state | |
useAnimatedReaction( | |
() => context.value, | |
(ctx) => { | |
if (ctx.includes(id)) { | |
// do stuff on item visible | |
} else if (!ctx.includes(id)) { | |
// do stuff on item invisible | |
} | |
}, | |
[] | |
); | |
} | |
Thanks for this, was a great performance boost 💯
context is the secret sauce to this. nice.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Neat, thanks for this!