import type React from "react"; import { StyleProp, ScrollViewProps, ViewabilityConfig, ViewStyle, } from "react-native"; import ViewToken from "./recyclerview/viewability/ViewToken"; export interface ListRenderItemInfo { item: TItem; index: number; /** * FlashList may render your items for multiple reasons. * Cell - This is for your list item * Measurement - Might be invoked for size measurement and won't be visible. You can ignore this in analytics. * StickyHeader - This is for your sticky header. Use this to change your item's appearance while it's being used as a sticky header. */ target: RenderTarget; extraData?: any; } export interface OverrideProps { initialDrawBatchSize?: number; // rest can be string to any [key: string]: any; } export type RenderTarget = "Cell" | "StickyHeader" | "Measurement"; export const RenderTargetOptions: Record = { Cell: "Cell", StickyHeader: "StickyHeader", Measurement: "Measurement", }; export type ListRenderItem = ( info: ListRenderItemInfo ) => React.ReactElement | null; export interface ViewabilityConfigCallbackPair { viewabilityConfig: ViewabilityConfig; onViewableItemsChanged: | ((info: { viewableItems: ViewToken[]; changed: ViewToken[]; }) => void) | null; } export type ViewabilityConfigCallbackPairs = ViewabilityConfigCallbackPair[]; export interface FlashListProps extends Omit { /** * Takes an item from `data` and renders it into the list. Typical usage: * ```ts * renderItem = ({item}) => ( * {item.title} * ); * ... * * ``` * * Provides additional metadata like `index` * * - `item` (`Object`): The item from `data` being rendered. * - `index` (`number`): The index corresponding to this item in the `data` array. */ renderItem: ListRenderItem | null | undefined; /** * For simplicity, data is a plain array of items of a given type. */ data: ReadonlyArray | null | undefined; /** * Each cell is rendered using this element. * Can be a React Component Class, or a render function. * The root component should always be a `CellContainer` which is also the default component used. * Ensure that the original `props` are passed to the returned `CellContainer`. The `props` will include the following: * - `onLayout`: Method for updating data about the real `CellContainer` layout * - `index`: Index of the cell in the list, you can use this to query data if needed * - `style`: Style of `CellContainer`, including: * - `flexDirection`: Depends on whether your list is horizontal or vertical * - `position`: Value of this will be `absolute` as that's how `FlashList` positions elements * - `left`: Determines position of the element on x axis * - `top`: Determines position of the element on y axis * - `width`: Determines width of the element (present when list is vertical) * - `height`: Determines height of the element (present when list is horizontal) * * Note: Changing layout of the cell can conflict with the native layout operations. You may need to set `disableAutoLayout` to `true` to prevent this. */ CellRendererComponent?: | React.ComponentType | React.ExoticComponent | undefined; /** * Rendered in between each item, but not at the top or bottom. By default, `leadingItem` and `trailingItem` (if available) props are provided. */ ItemSeparatorComponent?: | React.ComponentType | React.ExoticComponent | null | undefined; /** * Rendered when the list is empty. Can be a React Component (e.g. `SomeComponent`), or a React element (e.g. ``). */ ListEmptyComponent?: | React.ComponentType | React.ExoticComponent | React.ReactElement | null | undefined; /** * Styling for internal View for `ListEmptyComponent`. */ ListEmptyComponentStyle?: StyleProp | undefined; /** * Rendered at the bottom of all the items. Can be a React Component (e.g. `SomeComponent`), or a React element (e.g. ``). */ ListFooterComponent?: | React.ComponentType | React.ExoticComponent | React.ReactElement | null | undefined; /** * Styling for internal View for `ListFooterComponent`. */ ListFooterComponentStyle?: StyleProp | undefined; /** * Rendered at the top of all the items. Can be a React Component (e.g. `SomeComponent`), or a React element (e.g. ``). */ ListHeaderComponent?: | React.ComponentType | React.ExoticComponent | React.ReactElement | null | undefined; /** * Styling for internal View for `ListHeaderComponent`. */ ListHeaderComponentStyle?: StyleProp | undefined; /** * Rendered as the main scrollview. */ renderScrollComponent?: | React.ComponentType | React.ExoticComponent | React.FC; /** * Draw distance for advanced rendering (in dp/px) */ drawDistance?: number; /** * A marker property for telling the list to re-render (since it implements PureComponent). * If any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the `data` prop, * stick it here and treat it immutably. */ extraData?: any; /** * If true, renders items next to each other horizontally instead of stacked vertically. */ horizontal?: boolean | null | undefined; /** * Reverses the direction of scroll. Uses scale transforms of -1. * * Note: On Android, a rotate transform is used instead of scale for performance reasons. * This causes the scrollbar to appear on the left side of the list. */ inverted?: boolean | null | undefined; /** * Instead of starting at the top with the first item, start at initialScrollIndex. */ initialScrollIndex?: number | null | undefined; /** * Additional configuration for initialScrollIndex. * Use viewOffset to apply an offset to the initial scroll position as defined by initialScrollIndex. * Ignored if initialScrollIndex is not set. */ initialScrollIndexParams?: { viewOffset?: number } | null | undefined; /** * Used to extract a unique key for a given item at the specified index. * Key is used for optimizing performance. Defining `keyExtractor` is also necessary * when doing [layout animations](https://shopify.github.io/flash-list/docs/guides/layout-animation) * to uniquely identify animated components. */ keyExtractor?: ((item: TItem, index: number) => string) | undefined; /** * Multiple columns can only be rendered with `horizontal={false}` and will zig-zag like a `flexWrap` layout. * Items should all be the same height - masonry layouts are not supported. */ numColumns?: number | undefined; /** * Called once when the scroll position gets within onEndReachedThreshold of the rendered content. */ onEndReached?: (() => void) | null | undefined; /** * How far from the end (in units of visible length of the list) the bottom edge of the * list must be from the end of the content to trigger the `onEndReached` callback. * Thus a value of 0.5 will trigger `onEndReached` when the end of the content is * within half the visible length of the list. Default value is 0.5. */ onEndReachedThreshold?: number | null | undefined; /** * This event is raised once the list has drawn items on the screen. It also reports @param elapsedTimeInMs which is the time it took to draw the items. * This is required because FlashList doesn't render items in the first cycle. Items are drawn after it measures itself at the end of first render. * If you're using ListEmptyComponent, this event is raised as soon as ListEmptyComponent is rendered. */ onLoad?: (info: { elapsedTimeInMs: number }) => void; /** * Called when the viewability of rows changes, as defined by the `viewabilityConfig` prop. * Array of `changed` includes `ViewToken`s that both visible and non-visible items. You can use the `isViewable` flag to filter the items. * * If you are tracking the time a view becomes (non-)visible, use the `timestamp` property. * We make no guarantees that in the future viewability callbacks will be invoked as soon as they happen - for example, * they might be deferred until JS thread is less busy. */ onViewableItemsChanged?: | ((info: { viewableItems: ViewToken[]; changed: ViewToken[]; }) => void) | null | undefined; /** * If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. * Make sure to also set the refreshing prop correctly. */ onRefresh?: (() => void) | null | undefined; /** * Allows developers to override type of items. This will improve recycling if you have different types of items in the list * Right type will be used for the right item. Default type is 0 * If you don't want to change for an indexes just return undefined. * Performance: This method is called very frequently. Keep it fast. */ getItemType?: ( item: TItem, index: number, extraData?: any ) => string | number | undefined; /** * This method can be used to change column span of an item. * Changing item span is useful when you have grid layouts (numColumns > 1) and you want few items to be bigger than the rest. * * Modify the given layout. Do not return. FlashList will fallback to default values if this is ignored. * * Performance: This method is called very frequently. Keep it fast. */ overrideItemLayout?: ( layout: { span?: number }, item: TItem, index: number, maxColumns: number, extraData?: any ) => void; /** * Allows overriding internal ScrollView props. Props provided here are * spread onto the internal ScrollView after all other props, so they take * highest priority. * * This can be useful for cases where you need to set a style on the * ScrollView itself rather than the outer container. For example, to enable * visible overflow you can combine this with the `style` prop: * * ```tsx * * ``` * * Use with caution — overriding internal props may interfere with * FlashList's layout and recycling behavior. */ overrideProps?: OverrideProps; /** * Set this when offset is needed for the loading indicator to show correctly. * @platform android */ progressViewOffset?: number | undefined; /** * Set this true while waiting for new data from a refresh. */ refreshing?: boolean | null | undefined; /** * `viewabilityConfig` is a default configuration for determining whether items are viewable. * * Changing viewabilityConfig on the fly is not supported. */ viewabilityConfig?: ViewabilityConfig | null | undefined; /** * List of `ViewabilityConfig`/`onViewableItemsChanged` pairs. * A specific `onViewableItemsChanged` will be called when its corresponding `ViewabilityConfig`'s conditions are met. */ viewabilityConfigCallbackPairs?: | ViewabilityConfigCallbackPairs | undefined; /** * New arch only * Maximum number of items in the recycle pool. These are the items that are cached in the recycle pool when they are scrolled off the screen. * Unless you have a huge number of item types, you shouldn't need to set this. * Setting this to 0, will disable the recycle pool and items will unmount once they are scrolled off the screen. * There's no limit by default. */ maxItemsInRecyclePool?: number; /** * New arch only * Enable masonry layout. */ masonry?: boolean; /** * New arch only * If enabled, MasonryFlashList will try to reduce difference in column height by modifying item order. */ optimizeItemArrangement?: boolean; // TODO: Check if this breaks on item resize or is glitchy /** * New arch only * Called once when the scroll position gets within onStartReachedThreshold of the start of the content. */ onStartReached?: FlashListProps["onEndReached"]; /** * New arch only * How far from the start (in units of visible length of the list) the top edge of the * list must be from the start of the content to trigger the `onStartReached` callback. * Thus a value of 0.5 will trigger `onStartReached` when the start of the content is * within half the visible length of the list. Default value is 0.2. */ onStartReachedThreshold?: FlashListProps["onEndReachedThreshold"]; /** * New arch only * Style for the RecyclerView's parent container. * Please avoid anything which can mess size of children in this view. For example, margin is okay but padding is not. * * Note: To enable `overflow: "visible"`, you must also set it on the * internal ScrollView via `overrideProps`: * ```tsx * * ``` */ style?: StyleProp; /** * New arch only * Configuration for maintaining scroll position when content changes. * Useful for chat-like interfaces where new messages can be added at the top or bottom. */ maintainVisibleContentPosition?: { /** * maintainVisibleContentPosition is enabled by default. */ disabled?: boolean; /** * When content is added at the top, automatically scroll to maintain position if the user is within this threshold of the top */ autoscrollToTopThreshold?: number; /** * When content is added at the bottom, automatically scroll to maintain position if the user is within this threshold of the bottom */ autoscrollToBottomThreshold?: number; /** * Scroll with animation whenever `autoscrollToBottom` is triggered. Default is `true`. * Default is true. */ animateAutoScrollToBottom?: boolean; /** * If true, initial render will start from the bottom of the list, useful for chat-like interfaces when there are only few messages */ startRenderingFromBottom?: boolean; }; /** * New arch only * Called when the layout is committed. Can be used to measure list. * Doing set state inside the callback can lead to infinite loops. Make sure FlashList's props are memoized. */ onCommitLayoutEffect?: () => void; /** * Callback invoked when the currently displayed sticky header changes. * Receives the current sticky header index and the previous sticky header index. * This is useful for tracking which header is currently stuck at the top while scrolling. * The index refers to the position of the item in your data array that's being used as a sticky header. */ onChangeStickyIndex?: (current: number, previous: number) => void; stickyHeaderConfig?: | { /** * If true, the sticky headers will use native driver for animations. * @default true */ useNativeDriver?: boolean; /** * Offset from the top of the list where sticky headers should stick. * This is useful when you have a fixed header or navigation bar at the top of your screen * and want sticky headers to appear below it instead of at the very top. * @default 0 */ offset?: number; /** * Component to render behind sticky headers (e.g., a backdrop or blur effect). * Renders in front of the scroll view content but behind the sticky header itself. * Useful for creating visual separation or effects like backgrounds with blur. */ backdropComponent?: | React.ComponentType | React.ExoticComponent | React.ReactElement | null | undefined; /** * When a sticky header is displayed, the cell associated with it is hidden. * @default false */ hideRelatedCell?: boolean; } | undefined; }