create-expo-cljs-app
Version:
Create a react native application with Expo and Shadow-CLJS!
587 lines (550 loc) • 19.6 kB
Flow
/**
* Copyright (c) Facebook, Inc. and its 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 { ViewProps } from '../../../exports/View';
import type { ViewabilityConfig, ViewToken, ViewabilityConfigCallbackPair } from '../ViewabilityHelper';
import Batchinator from '../Batchinator';
import FillRateHelper from '../FillRateHelper';
import PropTypes from 'prop-types';
import * as React from 'react';
import RefreshControl from '../../../exports/RefreshControl';
import ScrollView from '../../../exports/ScrollView';
import StyleSheet from '../../../exports/StyleSheet';
import UIManager from '../../../exports/UIManager';
import View from '../../../exports/View';
import ViewabilityHelper from '../ViewabilityHelper';
import findNodeHandle from '../../../exports/findNodeHandle';
const flattenStyle = StyleSheet.flatten;
import infoLog from '../infoLog';
import invariant from 'fbjs/lib/invariant';
import warning from 'fbjs/lib/warning';
import { computeWindowedRenderLimits } from '../VirtualizeUtils';
import { VirtualizedListCellContextProvider, VirtualizedListContext, VirtualizedListContextProvider, type ChildListState, type ListDebugInfo } from './VirtualizedListContext.js';
type Item = any;
type ViewStyleProp = $PropertyType<ViewProps, 'style'>;
export type renderItemType = (info: any) => ?React.Element<any>;
const __DEV__ = process.env.NODE_ENV !== 'production';
export type Separators = {
highlight: () => void,
unhighlight: () => void,
updateProps: (select: 'leading' | 'trailing', newProps: Object) => void,
...
};
export type RenderItemProps<ItemT> = {
item: ItemT,
index: number,
separators: Separators,
...
};
export type RenderItemType<ItemT> = (info: RenderItemProps<ItemT>) => React.Node;
type ViewabilityHelperCallbackTuple = {
viewabilityHelper: ViewabilityHelper,
onViewableItemsChanged: (info: {
viewableItems: Array<ViewToken>,
changed: Array<ViewToken>,
...
}) => void,
...
};
type RequiredProps = {|
/**
* The default accessor functions assume this is an Array<{key: string} | {id: string}> but you can override
* getItem, getItemCount, and keyExtractor to handle any type of index-based data.
*/
data?: any,
/**
* A generic accessor for extracting an item from any sort of data blob.
*/
getItem: (data: any, index: number) => ?Item,
/**
* Determines how many items are in the data blob.
*/
getItemCount: (data: any) => number,
|};
type OptionalProps = {|
renderItem?: ?RenderItemType<Item>,
/**
* `debug` will turn on extra logging and visual overlays to aid with debugging both usage and
* implementation, but with a significant perf hit.
*/
debug?: ?boolean,
/**
* DEPRECATED: Virtualization provides significant performance and memory optimizations, but fully
* unmounts react instances that are outside of the render window. You should only need to disable
* this for debugging purposes.
*/
disableVirtualization?: ?boolean,
/**
* 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,
// e.g. height, y
getItemLayout?: (data: any, index: number) => {
length: number,
offset: number,
index: number,
...
},
horizontal?: ?boolean,
/**
* How many items to render in the initial batch. This should be enough to fill the screen but not
* much more. Note these items will never be unmounted as part of the windowed rendering in order
* to improve perceived performance of scroll-to-top actions.
*/
initialNumToRender: number,
/**
* Instead of starting at the top with the first item, start at `initialScrollIndex`. This
* disables the "scroll to top" optimization that keeps the first `initialNumToRender` items
* always rendered and immediately renders the items starting at this initial index. Requires
* `getItemLayout` to be implemented.
*/
initialScrollIndex?: ?number,
/**
* Reverses the direction of scroll. Uses scale transforms of -1.
*/
inverted?: ?boolean,
keyExtractor: (item: Item, index: number) => string,
/**
* Each cell is rendered using this element. Can be a React Component Class,
* or a render function. Defaults to using View.
*/
CellRendererComponent?: ?React.ComponentType<any>,
/**
* Rendered in between each item, but not at the top or bottom. By default, `highlighted` and
* `leadingItem` props are provided. `renderItem` provides `separators.highlight`/`unhighlight`
* which will update the `highlighted` prop, but you can also add custom props with
* `separators.updateProps`.
*/
ItemSeparatorComponent?: ?React.ComponentType<any>,
/**
* Takes an item from `data` and renders it into the list. Example usage:
*
* <FlatList
* ItemSeparatorComponent={Platform.OS !== 'android' && ({highlighted}) => (
* <View style={[style.separator, highlighted && {marginLeft: 0}]} />
* )}
* data={[{title: 'Title Text', key: 'item1'}]}
* ListItemComponent={({item, separators}) => (
* <TouchableHighlight
* onPress={() => this._onPress(item)}
* onShowUnderlay={separators.highlight}
* onHideUnderlay={separators.unhighlight}>
* <View style={{backgroundColor: 'white'}}>
* <Text>{item.title}</Text>
* </View>
* </TouchableHighlight>
* )}
* />
*
* Provides additional metadata like `index` if you need it, as well as a more generic
* `separators.updateProps` function which let's you set whatever props you want to change the
* rendering of either the leading separator or trailing separator in case the more common
* `highlight` and `unhighlight` (which set the `highlighted: boolean` prop) are insufficient for
* your use-case.
*/
ListItemComponent?: ?(React.ComponentType<any> | React.Element<any>),
/**
* Rendered when the list is empty. Can be a React Component Class, a render function, or
* a rendered element.
*/
ListEmptyComponent?: ?(React.ComponentType<any> | React.Element<any>),
/**
* Rendered at the bottom of all the items. Can be a React Component Class, a render function, or
* a rendered element.
*/
ListFooterComponent?: ?(React.ComponentType<any> | React.Element<any>),
/**
* Styling for internal View for ListFooterComponent
*/
ListFooterComponentStyle?: ViewStyleProp,
/**
* Rendered at the top of all the items. Can be a React Component Class, a render function, or
* a rendered element.
*/
ListHeaderComponent?: ?(React.ComponentType<any> | React.Element<any>),
/**
* Styling for internal View for ListHeaderComponent
*/
ListHeaderComponentStyle?: ViewStyleProp,
/**
* A unique identifier for this list. If there are multiple VirtualizedLists at the same level of
* nesting within another VirtualizedList, this key is necessary for virtualization to
* work properly.
*/
listKey?: string,
/**
* The maximum number of items to render in each incremental render batch. The more rendered at
* once, the better the fill rate, but responsiveness may suffer because rendering content may
* interfere with responding to button taps or other interactions.
*/
maxToRenderPerBatch: number,
/**
* Called once when the scroll position gets within `onEndReachedThreshold` of the rendered
* content.
*/
onEndReached?: ?(info: {
distanceFromEnd: number,
...
}) => void,
/**
* 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.
*/
onEndReachedThreshold?: ?number,
/**
* If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make
* sure to also set the `refreshing` prop correctly.
*/
onRefresh?: ?() => void,
/**
* Used to handle failures when scrolling to an index that has not been measured yet. Recommended
* action is to either compute your own offset and `scrollTo` it, or scroll as far as possible and
* then try again after more items have been rendered.
*/
onScrollToIndexFailed?: ?(info: {
index: number,
highestMeasuredFrameIndex: number,
averageItemLength: number,
...
}) => void,
/**
* Called when the viewability of rows changes, as defined by the
* `viewabilityConfig` prop.
*/
onViewableItemsChanged?: ?(info: {
viewableItems: Array<ViewToken>,
changed: Array<ViewToken>,
...
}) => void,
persistentScrollbar?: ?boolean,
/**
* Set this when offset is needed for the loading indicator to show correctly.
* @platform android
*/
progressViewOffset?: number,
/**
* A custom refresh control element. When set, it overrides the default
* <RefreshControl> component built internally. The onRefresh and refreshing
* props are also ignored. Only works for vertical VirtualizedList.
*/
refreshControl?: ?React.Element<any>,
/**
* Set this true while waiting for new data from a refresh.
*/
refreshing?: ?boolean,
/**
* Note: may have bugs (missing content) in some circumstances - use at your own risk.
*
* This may improve scroll performance for large lists.
*/
removeClippedSubviews?: boolean,
/**
* Render a custom scroll component, e.g. with a differently styled `RefreshControl`.
*/
renderScrollComponent?: (props: Object) => React.Element<any>,
/**
* Amount of time between low-pri item render batches, e.g. for rendering items quite a ways off
* screen. Similar fill rate/responsiveness tradeoff as `maxToRenderPerBatch`.
*/
updateCellsBatchingPeriod: number,
/**
* See `ViewabilityHelper` for flow type and further documentation.
*/
viewabilityConfig?: ViewabilityConfig,
/**
* List of ViewabilityConfig/onViewableItemsChanged pairs. A specific onViewableItemsChanged
* will be called when its corresponding ViewabilityConfig's conditions are met.
*/
viewabilityConfigCallbackPairs?: Array<ViewabilityConfigCallbackPair>,
/**
* Determines the maximum number of items rendered outside of the visible area, in units of
* visible lengths. So if your list fills the screen, then `windowSize={21}` (the default) will
* render the visible screen area plus up to 10 screens above and 10 below the viewport. Reducing
* this number will reduce memory consumption and may improve performance, but will increase the
* chance that fast scrolling may reveal momentary blank areas of unrendered content.
*/
windowSize: number,
/**
* The legacy implementation is no longer supported.
*/
legacyImplementation?: empty,
|};
type Props = {| // $FlowFixMe: View should be changed to an exact type in the future
...React.ElementConfig<typeof ScrollView>,
...RequiredProps,
...OptionalProps,
|};
type DefaultProps = {|
disableVirtualization: boolean,
horizontal: boolean,
initialNumToRender: number,
keyExtractor: (item: Item, index: number) => string,
maxToRenderPerBatch: number,
onEndReachedThreshold: number,
scrollEventThrottle: number,
updateCellsBatchingPeriod: number,
windowSize: number,
|};
let _usedIndexForKey = false;
let _keylessItemComponentName: string = '';
type State = {
first: number,
last: number,
};
/**
* Base implementation for the more convenient [`<FlatList>`](https://reactnative.dev/docs/flatlist.html)
* and [`<SectionList>`](https://reactnative.dev/docs/sectionlist.html) 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.
*
*/
declare class VirtualizedList extends React.PureComponent<Props, State> {
static contextType: typeof VirtualizedListContext,
scrollToEnd(params?: ?{
animated?: ?boolean,
...
}): any,
scrollToIndex(params: {
animated?: ?boolean,
index: number,
viewOffset?: number,
viewPosition?: number,
...
}): any,
scrollToItem(params: {
animated?: ?boolean,
item: Item,
viewPosition?: number,
...
}): any,
scrollToOffset(params: {
animated?: ?boolean,
offset: number,
...
}): any,
recordInteraction(): any,
flashScrollIndicators(): any,
getScrollResponder(): ?typeof ScrollView,
getScrollableNode(): ?number,
getScrollRef(): ?React.ElementRef<typeof ScrollView> | ?React.ElementRef<typeof View>,
setNativeProps(props: Object): any,
static defaultProps: DefaultProps,
_getCellKey(): string,
_getListKey(): string,
_getDebugInfo(): ListDebugInfo,
_getScrollMetrics: any,
hasMore(): boolean,
_getOutermostParentListRef: any,
_getNestedChildState: any,
_registerAsNestedChild: any,
_unregisterAsNestedChild: any,
state: State,
constructor(props: Props): any,
componentDidMount(): any,
componentWillUnmount(): 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,
_onUpdateSeparators: any,
_isVirtualizationDisabled(): boolean,
_isNestedWithSameOrientation(): boolean,
render(): React.Node,
componentDidUpdate(prevProps: Props): any,
_averageCellLength: any,
_cellKeysToChildListKeys: Map<string, Set<string>>,
_cellRefs: any,
_fillRateHelper: FillRateHelper,
_frames: any,
_footerLength: any,
_hasDoneInitialScroll: any,
_hasInteracted: any,
_hasMore: any,
_hasWarned: any,
_headerLength: any,
_hiPriInProgress: boolean,
_highestMeasuredFrameIndex: any,
_indicesToKeys: Map<number, string>,
_nestedChildLists: Map<string, {
ref: ?VirtualizedList,
state: ?ChildListState,
...
}>,
_offsetFromParentVirtualizedList: number,
_prevParentOffset: number,
_scrollMetrics: any,
_scrollRef: ?React.ElementRef<any>,
_sentEndForContentLength: any,
_totalCellLength: any,
_totalCellsMeasured: any,
_updateCellsToRenderBatcher: Batchinator,
_viewabilityTuples: Array<ViewabilityHelperCallbackTuple>,
_captureScrollRef: any,
_computeBlankness(): any,
_defaultRenderScrollComponent: any,
_onCellLayout(e: any, cellKey: any, index: any): 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,
_maybeCallOnEndReached(): any,
_onContentSizeChange: any,
_convertParentScrollMetrics: any,
_onScroll: any,
_scheduleCellsToRenderUpdate(): any,
_onScrollBeginDrag: any,
_onScrollEndDrag: any,
_onMomentumScrollBegin: any,
_onMomentumScrollEnd: any,
_updateCellsToRender: any,
_createViewToken: any,
_getFrameMetricsApprox: any,
_getFrameMetrics: any,
_updateViewableItems(data: any): any,
}
type CellRendererProps = {
CellRendererComponent?: ?React.ComponentType<any>,
ItemSeparatorComponent: ?React.ComponentType<*>,
cellKey: string,
fillRateHelper: FillRateHelper,
horizontal: ?boolean,
index: number,
inversionStyle: ViewStyleProp,
item: Item,
// This is extracted by ScrollViewStickyHeader
onLayout: (event: Object) => void,
onUnmount: (cellKey: string) => void,
onUpdateSeparators: (cellKeys: Arraystring>, props: Object) => void,
parentProps: {
// e.g. height, y,
getItemLayout?: (data: any, index: number) => {
length: number,
offset: number,
index: number,
...
},
renderItem?: ?RenderItemType<Item>,
ListItemComponent?: ?(React.ComponentType<any> | React.Element<any>),
...
},
prevCellKey: ?string,
...
};
type CellRendererState = {
separatorProps: $ReadOnly<{|
highlighted: boolean,
leadingItem: ?Item,
|}>,
...
};
declare class CellRenderer extends React.Component<CellRendererProps, CellRendererState> {
state: any,
static getDerivedStateFromProps(props: CellRendererProps, prevState: CellRendererState): ?CellRendererState,
_separators: any,
updateSeparatorProps(newProps: Object): any,
componentWillUnmount(): any,
_renderElement(renderItem: any, ListItemComponent: any, item: any, index: any): any,
render(): any,
}
declare function describeNestedLists(childList: {
+cellKey: string,
+key: string,
+ref: VirtualizedList,
+parentDebugInfo: ListDebugInfo,
+horizontal: boolean,
...
}): any;
const styles = StyleSheet.create({
verticallyInverted: {
transform: [{
scaleY: -1
}]
},
horizontallyInverted: {
transform: [{
scaleX: -1
}]
},
row: {
flexDirection: 'row'
},
rowReverse: {
flexDirection: 'row-reverse'
},
columnReverse: {
flexDirection: 'column-reverse'
},
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;