import { CSSProperties, forwardRef, memo, useCallback, useRef } from 'react'; import { VariableSizeList as ReactWindowList, ListChildComponentProps, ListOnScrollProps } from 'react-window'; import InfiniteLoader from 'react-window-infinite-loader'; import { IVariableSizedListProps, ListRef } from './type'; /** * Web `List` — thin wrapper around `react-window`'s `VariableSizeList` * + `react-window-infinite-loader` for lazy loading. * * NOTE: animation hooks on the web side are intentionally not wired up yet; * see the TODO in the original yagami implementation. */ const List = memo( forwardRef>( ( { loadMoreItems, data, minimumBatchSize, threshold, itemCount, itemSize, itemData, RenderItem, height, width, style, outerElementType, overScanCount, onScroll, }, // react-window's ref is a VariableSizeList instance; the public ListRef // is only partially satisfied (scrollToEnd isn't exposed by react-window // directly). Consumers that need it typically wrap and call scrollTo. _forwardedRef, ) => { const listRef = useRef>(null); const isRowLoaded = (index: number) => { return index < data.length; }; // TODO: add web-based implementation for row enter / exit animations. const RenderListItem = useCallback(({ data: listData, index }: ListChildComponentProps<{ data: unknown[] }>) => { return ; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( // nicely with React 18's children-as-function pattern here. {({ onItemsRendered }) => { return ( // data generic which we relax here for wrapper ergonomics. 0)} itemData={itemData} ref={listRef} style={style as CSSProperties} // outerElementType at runtime. outerElementType={outerElementType} overscanCount={overScanCount} useIsScrolling onItemsRendered={(params) => { onItemsRendered(params); }} onScroll={(_e: ListOnScrollProps) => { onScroll?.({ nativeEvent: { contentOffset: { x: 0, y: 0, }, layoutMeasurement: { height: 0, width: 0, }, contentSize: { height: 0, width: 0, }, }, }); }} > {RenderListItem} ); }} ); }, ), ) as unknown as ( props: IVariableSizedListProps & { ref?: React.Ref }, ) => React.JSX.Element; (List as unknown as { displayName: string }).displayName = 'List'; export default List;