react-native-web
Version:
React Native for Web
303 lines (300 loc) • 11 kB
Flow
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import type { LayoutEvent } from '../../../types';
import type { ScrollEvent } from '../Types/CoreEventTypes';
import type { ViewToken } from '../ViewabilityHelper';
import type { FrameMetricProps, Item, Props, RenderItemProps, RenderItemType, Separators } from './VirtualizedListProps';
import RefreshControl from '../../../exports/RefreshControl';
import ScrollView from '../../../exports/ScrollView';
import View, { type ViewProps } from '../../../exports/View';
import StyleSheet from '../../../exports/StyleSheet';
import Batchinator from '../Batchinator';
import clamp from '../Utilities/clamp';
import infoLog from '../infoLog';
import { CellRenderMask } from './CellRenderMask';
import ChildListCollection from './ChildListCollection';
import FillRateHelper from '../FillRateHelper';
import StateSafePureComponent from './StateSafePureComponent';
import ViewabilityHelper from '../ViewabilityHelper';
import CellRenderer from './VirtualizedListCellRenderer';
import { VirtualizedListCellContextProvider, VirtualizedListContext, VirtualizedListContextProvider } from './VirtualizedListContext.js';
import { computeWindowedRenderLimits, keyExtractor as defaultKeyExtractor } from '../VirtualizeUtils';
import invariant from 'fbjs/lib/invariant';
import nullthrows from 'nullthrows';
import * as React from 'react';
export type { RenderItemProps, RenderItemType, Separators };
const __DEV__ = process.env.NODE_ENV !== 'production';
const ON_EDGE_REACHED_EPSILON = 0.001;
let _usedIndexForKey = false;
let _keylessItemComponentName: string = '';
type ScrollResponderType = any;
type ViewStyleProp = $PropertyType<ViewProps, 'style'>;
type ViewabilityHelperCallbackTuple = {
viewabilityHelper: ViewabilityHelper,
onViewableItemsChanged: (info: {
viewableItems: Array<ViewToken>,
changed: Array<ViewToken>,
...
}) => void,
...
};
type State = {
renderMask: CellRenderMask,
cellsAroundViewport: {
first: number,
last: number,
},
};
/**
* Default Props Helper Functions
* Use the following helper functions for default values
*/
// horizontalOrDefault(this.props.horizontal)
declare function horizontalOrDefault(horizontal: ?boolean): any; // initialNumToRenderOrDefault(this.props.initialNumToRender)
declare function initialNumToRenderOrDefault(initialNumToRender: ?number): any; // maxToRenderPerBatchOrDefault(this.props.maxToRenderPerBatch)
declare function maxToRenderPerBatchOrDefault(maxToRenderPerBatch: ?number): any; // onStartReachedThresholdOrDefault(this.props.onStartReachedThreshold)
declare function onStartReachedThresholdOrDefault(onStartReachedThreshold: ?number): any; // onEndReachedThresholdOrDefault(this.props.onEndReachedThreshold)
declare function onEndReachedThresholdOrDefault(onEndReachedThreshold: ?number): any; // getScrollingThreshold(visibleLength, onEndReachedThreshold)
declare function getScrollingThreshold(threshold: number, visibleLength: number): any; // scrollEventThrottleOrDefault(this.props.scrollEventThrottle)
declare function scrollEventThrottleOrDefault(scrollEventThrottle: ?number): any; // windowSizeOrDefault(this.props.windowSize)
declare function windowSizeOrDefault(windowSize: ?number): any;
declare function findLastWhere<T>(arr: $ReadOnlyArray<T>, predicate: (element: T) => boolean): T | null;
/**
* Base implementation for the more convenient [`<FlatList>`](https://reactnative.dev/docs/flatlist)
* and [`<SectionList>`](https://reactnative.dev/docs/sectionlist) components, which are also better
* documented. In general, this should only really be used if you need more flexibility than
* `FlatList` provides, e.g. for use with immutable data instead of plain arrays.
*
* Virtualization massively improves memory consumption and performance of large lists by
* maintaining a finite render window of active items and replacing all items outside of the render
* window with appropriately sized blank space. The window adapts to scrolling behavior, and items
* are rendered incrementally with low-pri (after any running interactions) if they are far from the
* visible area, or with hi-pri otherwise to minimize the potential of seeing blank space.
*
* Some caveats:
*
* - Internal state is not preserved when content scrolls out of the render window. Make sure all
* your data is captured in the item data or external stores like Flux, Redux, or Relay.
* - This is a `PureComponent` which means that it will not re-render if `props` remain shallow-
* equal. Make sure that everything your `renderItem` function depends on is passed as a prop
* (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on
* changes. This includes the `data` prop and parent component state.
* - In order to constrain memory and enable smooth scrolling, content is rendered asynchronously
* offscreen. This means it's possible to scroll faster than the fill rate ands momentarily see
* blank content. This is a tradeoff that can be adjusted to suit the needs of each application,
* and we are working on improving it behind the scenes.
* - By default, the list looks for a `key` or `id` prop on each item and uses that for the React key.
* Alternatively, you can provide a custom `keyExtractor` prop.
* - As an effort to remove defaultProps, use helper functions when referencing certain props
*
*/
declare class VirtualizedList extends StateSafePureComponent<Props, State> {
static contextType: typeof VirtualizedListContext,
scrollToEnd(params?: ?{
animated?: ?boolean,
...
}): any,
scrollToIndex(params: {
animated?: ?boolean,
index: number,
viewOffset?: number,
viewPosition?: number,
...
}): $FlowFixMe,
scrollToItem(params: {
animated?: ?boolean,
item: Item,
viewOffset?: number,
viewPosition?: number,
...
}): any,
scrollToOffset(params: {
animated?: ?boolean,
offset: number,
...
}): any,
recordInteraction(): any,
flashScrollIndicators(): any,
getScrollResponder(): ?ScrollResponderType,
getScrollableNode(): ?number,
getScrollRef(): ?React.ElementRef<typeof ScrollView> | ?React.ElementRef<typeof View>,
_getCellKey(): string,
_getScrollMetrics: any,
hasMore(): boolean,
_getOutermostParentListRef: any,
_registerAsNestedChild: any,
_unregisterAsNestedChild: any,
state: State,
invertedWheelEventHandler: ?(ev: any) => void,
constructor(props: Props): any,
_checkProps(props: Props): any,
static _createRenderMask(props: Props, cellsAroundViewport: {
first: number,
last: number,
}, additionalRegions?: ?$ReadOnlyArray<{
first: number,
last: number,
}>): CellRenderMask,
static _initialRenderRegion(props: Props): {
first: number,
last: number,
},
static _ensureClosestStickyHeader(props: Props, stickyIndicesSet: Set<number>, renderMask: CellRenderMask, cellIdx: number): any,
_adjustCellsAroundViewport(props: Props, cellsAroundViewport: {
first: number,
last: number,
}): {
first: number,
last: number,
},
_findFirstChildWithMore(first: number, last: number): number | null,
componentDidMount(): any,
componentWillUnmount(): any,
setupWebWheelHandler(): any,
teardownWebWheelHandler(): any,
static getDerivedStateFromProps(newProps: Props, prevState: State): State,
_pushCells(cells: Array<Object>, stickyHeaderIndices: Array<number>, stickyIndicesFromProps: Set<number>, first: number, last: number, inversionStyle: ViewStyleProp): any,
static _constrainToItemCount(cells: {
first: number,
last: number,
}, props: Props): {
first: number,
last: number,
},
_onUpdateSeparators: any,
_isNestedWithSameOrientation(): boolean,
_getSpacerKey: any,
_keyExtractor(item: Item, index: number, props: {
keyExtractor?: ?(item: Item, index: number) => string,
...
}): any,
render(): React.Node,
componentDidUpdate(prevProps: Props): any,
_averageCellLength: any,
_cellRefs: {
[string]: null | CellRenderer<any>
},
_fillRateHelper: FillRateHelper,
_frames: {
[string]: {
inLayout?: boolean,
index: number,
length: number,
offset: number,
}
},
_footerLength: any,
_hasTriggeredInitialScrollToIndex: any,
_hasInteracted: any,
_hasMore: any,
_hasWarned: {
[string]: boolean
},
_headerLength: any,
_hiPriInProgress: boolean,
_highestMeasuredFrameIndex: any,
_indicesToKeys: Map<number, string>,
_lastFocusedCellKey: ?string,
_nestedChildLists: ChildListCollection<VirtualizedList>,
_offsetFromParentVirtualizedList: number,
_prevParentOffset: number,
_scrollMetrics: any,
_scrollRef: ?React.ElementRef<any>,
_sentStartForContentLength: any,
_sentEndForContentLength: any,
_totalCellLength: any,
_totalCellsMeasured: any,
_updateCellsToRenderBatcher: Batchinator,
_viewabilityTuples: Array<ViewabilityHelperCallbackTuple>,
_captureScrollRef: any,
_computeBlankness(): any,
_defaultRenderScrollComponent: any,
_onCellLayout: any,
_onCellFocusCapture(cellKey: string): any,
_onCellUnmount: any,
_triggerRemeasureForChildListsInCell(cellKey: string): void,
measureLayoutRelativeToContainingList(): void,
_onLayout: any,
_onLayoutEmpty: any,
_getFooterCellKey(): string,
_onLayoutFooter: any,
_onLayoutHeader: any,
_renderDebugOverlay(): any,
_selectLength(metrics: $ReadOnly<{
height: number,
width: number,
...
}>): number,
_selectOffset(metrics: $ReadOnly<{
x: number,
y: number,
...
}>): number,
_maybeCallOnEdgeReached(): any,
_onContentSizeChange: any,
_convertParentScrollMetrics: any,
_onScroll: any,
_scheduleCellsToRenderUpdate(): any,
_onScrollBeginDrag: any,
_onScrollEndDrag: any,
_onMomentumScrollBegin: any,
_onMomentumScrollEnd: any,
_updateCellsToRender: any,
_createViewToken: any,
_getOffsetApprox: any,
__getFrameMetricsApprox: (index: number, props: FrameMetricProps) => {
length: number,
offset: number,
...
},
_getFrameMetrics: any,
_getNonViewportRenderRegions: any,
_updateViewableItems(props: FrameMetricProps, cellsAroundViewport: {
first: number,
last: number,
}): any,
}
const styles = StyleSheet.create({
verticallyInverted: {
transform: 'scaleY(-1)'
},
horizontallyInverted: {
transform: 'scaleX(-1)'
},
debug: {
flex: 1
},
debugOverlayBase: {
position: 'absolute',
top: 0,
right: 0
},
debugOverlay: {
bottom: 0,
width: 20,
borderColor: 'blue',
borderWidth: 1
},
debugOverlayFrame: {
left: 0,
backgroundColor: 'orange'
},
debugOverlayFrameLast: {
left: 0,
borderColor: 'green',
borderWidth: 2
},
debugOverlayFrameVis: {
left: 0,
borderColor: 'red',
borderWidth: 2
}
});
export default VirtualizedList;