Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client

[Lighbox perf - 1] Fix memoization of List component, defer re-rendering when opening lightbox (#9759)

* memoize FlatList_INTERNAL

* defer render, put handleScrolledDownChange in non reactive callback

* lintfix useDedupe

authored by samuel.fm and committed by

GitHub 3b2573bc ae77320d

+17 -15
+4 -4
src/lib/hooks/useDedupe.ts
··· 1 - import React from 'react' 1 + import {useCallback, useRef} from 'react' 2 2 3 - export const useDedupe = (timeout = 250) => { 4 - const canDo = React.useRef(true) 3 + export function useDedupe(timeout = 250) { 4 + const canDo = useRef(true) 5 5 6 - return React.useCallback( 6 + return useCallback( 7 7 (cb: () => unknown) => { 8 8 if (canDo.current) { 9 9 canDo.current = false
+10 -7
src/view/com/util/List.tsx
··· 1 - import React, {memo} from 'react' 1 + import {forwardRef, memo, useDeferredValue, useMemo} from 'react' 2 2 import {RefreshControl, type ViewToken} from 'react-native' 3 3 import { 4 4 type FlatListPropsWithLayout, ··· 9 9 import {updateActiveVideoViewAsync} from '@haileyok/bluesky-video' 10 10 11 11 import {useDedupe} from '#/lib/hooks/useDedupe' 12 + import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 12 13 import {useScrollHandlers} from '#/lib/ScrollContext' 13 14 import {addStyle} from '#/lib/styles' 14 15 import {useLightbox} from '#/state/lightbox' ··· 43 44 44 45 const SCROLLED_DOWN_LIMIT = 200 45 46 46 - let List = React.forwardRef<ListMethods, ListProps>( 47 + let List = forwardRef<ListMethods, ListProps>( 47 48 ( 48 49 { 49 50 onScrolledDownChange, ··· 63 64 const dedupe = useDedupe(400) 64 65 const scrollsToTop = useAllowScrollToTop() 65 66 66 - function handleScrolledDownChange(didScrollDown: boolean) { 67 - onScrolledDownChange?.(didScrollDown) 68 - } 67 + const handleScrolledDownChange = useNonReactiveCallback( 68 + (didScrollDown: boolean) => { 69 + onScrolledDownChange?.(didScrollDown) 70 + }, 71 + ) 69 72 70 73 // Intentionally destructured outside the main thread closure. 71 74 // See https://github.com/bluesky-social/social-app/pull/4108. ··· 106 109 }, 107 110 }) 108 111 109 - const [onViewableItemsChanged, viewabilityConfig] = React.useMemo(() => { 112 + const [onViewableItemsChanged, viewabilityConfig] = useMemo(() => { 110 113 if (!onItemSeen) { 111 114 return [undefined, undefined] 112 115 } ··· 187 190 const useAllowScrollToTop = IS_IOS ? useAllowScrollToTopIOS : () => undefined 188 191 function useAllowScrollToTopIOS() { 189 192 const {activeLightbox} = useLightbox() 190 - return !activeLightbox 193 + return useDeferredValue(!activeLightbox) 191 194 }
+3 -4
src/view/com/util/Views.tsx
··· 1 - import {forwardRef} from 'react' 2 - import {type FlatListComponent} from 'react-native' 3 - import {View, type ViewProps} from 'react-native' 1 + import {forwardRef, memo} from 'react' 2 + import {type FlatListComponent, View, type ViewProps} from 'react-native' 4 3 import Animated from 'react-native-reanimated' 5 4 import {type FlatListPropsWithLayout} from 'react-native-reanimated' 6 5 ··· 10 9 * Avoid using `FlatList_INTERNAL` and use `List` where possible. 11 10 * The types are a bit wrong on `FlatList_INTERNAL` 12 11 */ 13 - export const FlatList_INTERNAL = Animated.FlatList 12 + export const FlatList_INTERNAL = memo(Animated.FlatList) 14 13 export type FlatList_INTERNAL<ItemT = any> = Omit< 15 14 FlatListComponent<ItemT, FlatListPropsWithLayout<ItemT>>, 16 15 'CellRendererComponent'