11import type { FlashListProps } from '@shopify/flash-list' ;
22import { useEffect , useState } from 'react' ;
33
4+ // How long to keep MVCP enabled after `shouldMaintainVisibleContentPosition` drops back to false
5+ const MVCP_FALLOFF_MS = 500 ;
6+
47type FlashListScrollKeyProps < T > = {
58 /** The array of items to render in the list. */
69 data : T [ ] ;
@@ -13,9 +16,12 @@ type FlashListScrollKeyProps<T> = {
1316
1417 /** Callback invoked when the user scrolls close to the start of the list. */
1518 onStartReached : FlashListProps < T > [ 'onStartReached' ] ;
19+
20+ /** Whether the list should handle `maintainVisibleContentPosition` */
21+ shouldMaintainVisibleContentPosition ?: boolean ;
1622} ;
1723
18- export default function useFlashListScrollKey < T > ( { data, keyExtractor, initialScrollKey, onStartReached} : FlashListScrollKeyProps < T > ) {
24+ export default function useFlashListScrollKey < T > ( { data, keyExtractor, initialScrollKey, onStartReached, shouldMaintainVisibleContentPosition } : FlashListScrollKeyProps < T > ) {
1925 const [ isInitialRender , setIsInitialRender ] = useState ( true ) ;
2026 const [ hasLinkingSettled , setHasLinkingSettled ] = useState ( ! initialScrollKey ) ;
2127
@@ -33,9 +39,20 @@ export default function useFlashListScrollKey<T>({data, keyExtractor, initialScr
3339 } ) ;
3440 } , [ isInitialRender , initialScrollKey ] ) ;
3541
36- // `undefined` = leave FlashList's default (MVCP enabled) while we're still pinning the linked item.
37- // `{disabled: true}` once that's done so MVCP can't interfere afterward.
38- const maintainVisibleContentPosition : FlashListProps < T > [ 'maintainVisibleContentPosition' ] = hasLinkingSettled ? { disabled : true } : undefined ;
42+ // FlashList captures MVCP anchors on the render BEFORE a data change. Keep MVCP on at mount
43+ // (warmup) and while the caller raises the prop, then disable after a short falloff.
44+ const [ isKeepingMVCPOn , setIsKeepingMVCPOn ] = useState ( true ) ;
45+ useEffect ( ( ) => {
46+ if ( shouldMaintainVisibleContentPosition ) {
47+ setIsKeepingMVCPOn ( true ) ;
48+ return ;
49+ }
50+ const timeoutID = setTimeout ( ( ) => setIsKeepingMVCPOn ( false ) , MVCP_FALLOFF_MS ) ;
51+ return ( ) => clearTimeout ( timeoutID ) ;
52+ } , [ shouldMaintainVisibleContentPosition ] ) ;
53+
54+ const shouldEnableMVCP = ! ! shouldMaintainVisibleContentPosition || isKeepingMVCPOn || ! hasLinkingSettled ;
55+ const maintainVisibleContentPosition : FlashListProps < T > [ 'maintainVisibleContentPosition' ] = { disabled : ! shouldEnableMVCP } ;
3956
4057 if ( ! isInitialRender || ! initialScrollKey ) {
4158 return { displayedData : data , onStartReached, maintainVisibleContentPosition} ;
0 commit comments