···11-import React from 'react'
11+import {useCallback, useRef} from 'react'
2233-export const useDedupe = (timeout = 250) => {
44- const canDo = React.useRef(true)
33+export function useDedupe(timeout = 250) {
44+ const canDo = useRef(true)
5566- return React.useCallback(
66+ return useCallback(
77 (cb: () => unknown) => {
88 if (canDo.current) {
99 canDo.current = false
+10-7
src/view/com/util/List.tsx
···11-import React, {memo} from 'react'
11+import {forwardRef, memo, useDeferredValue, useMemo} from 'react'
22import {RefreshControl, type ViewToken} from 'react-native'
33import {
44 type FlatListPropsWithLayout,
···99import {updateActiveVideoViewAsync} from '@haileyok/bluesky-video'
10101111import {useDedupe} from '#/lib/hooks/useDedupe'
1212+import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
1213import {useScrollHandlers} from '#/lib/ScrollContext'
1314import {addStyle} from '#/lib/styles'
1415import {useLightbox} from '#/state/lightbox'
···43444445const SCROLLED_DOWN_LIMIT = 200
45464646-let List = React.forwardRef<ListMethods, ListProps>(
4747+let List = forwardRef<ListMethods, ListProps>(
4748 (
4849 {
4950 onScrolledDownChange,
···6364 const dedupe = useDedupe(400)
6465 const scrollsToTop = useAllowScrollToTop()
65666666- function handleScrolledDownChange(didScrollDown: boolean) {
6767- onScrolledDownChange?.(didScrollDown)
6868- }
6767+ const handleScrolledDownChange = useNonReactiveCallback(
6868+ (didScrollDown: boolean) => {
6969+ onScrolledDownChange?.(didScrollDown)
7070+ },
7171+ )
69727073 // Intentionally destructured outside the main thread closure.
7174 // See https://github.com/bluesky-social/social-app/pull/4108.
···106109 },
107110 })
108111109109- const [onViewableItemsChanged, viewabilityConfig] = React.useMemo(() => {
112112+ const [onViewableItemsChanged, viewabilityConfig] = useMemo(() => {
110113 if (!onItemSeen) {
111114 return [undefined, undefined]
112115 }
···187190const useAllowScrollToTop = IS_IOS ? useAllowScrollToTopIOS : () => undefined
188191function useAllowScrollToTopIOS() {
189192 const {activeLightbox} = useLightbox()
190190- return !activeLightbox
193193+ return useDeferredValue(!activeLightbox)
191194}
+3-4
src/view/com/util/Views.tsx
···11-import {forwardRef} from 'react'
22-import {type FlatListComponent} from 'react-native'
33-import {View, type ViewProps} from 'react-native'
11+import {forwardRef, memo} from 'react'
22+import {type FlatListComponent, View, type ViewProps} from 'react-native'
43import Animated from 'react-native-reanimated'
54import {type FlatListPropsWithLayout} from 'react-native-reanimated'
65···109 * Avoid using `FlatList_INTERNAL` and use `List` where possible.
1110 * The types are a bit wrong on `FlatList_INTERNAL`
1211 */
1313-export const FlatList_INTERNAL = Animated.FlatList
1212+export const FlatList_INTERNAL = memo(Animated.FlatList)
1413export type FlatList_INTERNAL<ItemT = any> = Omit<
1514 FlatListComponent<ItemT, FlatListPropsWithLayout<ItemT>>,
1615 'CellRendererComponent'