@@ -9,8 +9,10 @@ import {
99} from '@shopify/flash-list' ;
1010import React , {
1111 cloneElement ,
12+ forwardRef ,
1213 isValidElement ,
1314 useEffect ,
15+ useImperativeHandle ,
1416 useRef ,
1517 useState ,
1618} from 'react' ;
@@ -43,22 +45,39 @@ export type ListProps<T> = FlashListProps<T> & {
4345 | undefined ;
4446} ;
4547
46- export function List < T extends PostData = PostData > ( props : ListProps < T > ) {
48+ /** Imperative handle exposed by `List` (and `FeedList`). */
49+ export type ListRef = { scrollToTop : ( ) => void } ;
50+
51+ export const List = forwardRef ( function List < T extends PostData = PostData > (
52+ props : ListProps < T > ,
53+ ref : React . Ref < ListRef > ,
54+ ) {
4755 if ( isWeb ) {
48- return < WebFeedViewer < T > { ...props } /> ;
56+ return < WebFeedViewer < T > { ...props } listRef = { ref } /> ;
4957 }
5058
51- return < NativeList < T > { ...props } /> ;
52- }
59+ return < NativeList < T > { ...props } listRef = { ref } /> ;
60+ } ) as < T extends PostData = PostData > (
61+ props : ListProps < T > & { ref ?: React . Ref < ListRef > } ,
62+ ) => React . ReactElement ;
5363
5464function NativeList < T > ( {
5565 HeaderComponent,
5666 contentContainerStyle,
5767 refreshControl,
5868 onScroll : _ignoredOnScroll ,
69+ listRef,
5970 ...rest
60- } : ListProps < T > ) {
71+ } : ListProps < T > & { listRef ?: React . Ref < ListRef > } ) {
6172 const ref = useRef < FlashListRef < T > > ( null ) ;
73+ useImperativeHandle (
74+ listRef ,
75+ ( ) => ( {
76+ scrollToTop : ( ) =>
77+ ref . current ?. scrollToOffset ( { offset : 0 , animated : true } ) ,
78+ } ) ,
79+ [ ] ,
80+ ) ;
6281 const lastScrollY = useSharedValue ( 0 ) ;
6382 const headerTranslate = useSharedValue ( 0 ) ;
6483 const headerHeightShared = useSharedValue ( 0 ) ;
@@ -139,6 +158,7 @@ function NativeList<T>({
139158 ) : null }
140159
141160 < AnimatedFlashList
161+ ref = { ref as React . Ref < FlashListRef < unknown > > }
142162 { ...( rest as FlashListProps < unknown > ) }
143163 refreshControl = { adjustedRefreshControl }
144164 onScroll = { onScroll }
@@ -166,8 +186,20 @@ function WebFeedViewer<T>({
166186 onEndReached,
167187 contentContainerStyle,
168188 stickyHeaderIndices,
169- } : ListProps < T > ) {
189+ listRef,
190+ } : ListProps < T > & { listRef ?: React . Ref < ListRef > } ) {
170191 const sentinelRef = useRef < View > ( null ) ;
192+ useImperativeHandle (
193+ listRef ,
194+ ( ) => ( {
195+ scrollToTop : ( ) => {
196+ if ( typeof window !== 'undefined' ) {
197+ window . scrollTo ( { top : 0 , behavior : 'smooth' } ) ;
198+ }
199+ } ,
200+ } ) ,
201+ [ ] ,
202+ ) ;
171203 const items = ( data as readonly T [ ] | null | undefined ) ?? [ ] ;
172204 const [ visibleCount , setVisibleCount ] = useState ( WEB_INITIAL_VISIBLE ) ;
173205
0 commit comments