Skip to content

Instantly share code, notes, and snippets.

@intergalacticspacehighway
Last active January 23, 2024 03:38
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save intergalacticspacehighway/02a36b05b2236bc750a065833b71c94a to your computer and use it in GitHub Desktop.
Save intergalacticspacehighway/02a36b05b2236bc750a065833b71c94a to your computer and use it in GitHub Desktop.
Viewability tracker with shared values
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
}
},
[]
);
}
@hirbod
Copy link

hirbod commented Jan 25, 2022

Thanks for sharing. Interesting approach.
P.S: ViewToken is imported but unused :P

@daxaxelrod
Copy link

Neat, thanks for this!

@abdullahIsa
Copy link

Thanks for this, was a great performance boost 💯

@sebringj
Copy link

sebringj commented Nov 9, 2023

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