Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated VirtualizedList vendor files (with FlatList, SectionList) #2241

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 94 additions & 63 deletions packages/react-native-web/src/vendor/react-native/FlatList/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
* 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.
Expand All @@ -8,23 +8,26 @@
* @format
*/

import type {ViewProps} from '../../../exports/View';
import Platform from '../../../exports/Platform';
import deepDiffer from '../deepDiffer';
import * as React from 'react';
import View, { type ViewProps } from '../../../exports/View';
import VirtualizedList from '../VirtualizedList';
import StyleSheet from '../../../exports/StyleSheet';

import invariant from 'fbjs/lib/invariant';

type ScrollViewNativeComponent = any;
type ScrollResponderType = any;
type ViewStyleProp = $PropertyType<ViewProps, 'style'>;
import type {
ViewabilityConfig,
ViewToken,
ViewabilityConfigCallbackPair,
} from '../ViewabilityHelper';
import type {RenderItemType, RenderItemProps} from '../VirtualizedList';
import {keyExtractor as defaultKeyExtractor} from '../VirtualizeUtils';

import deepDiffer from '../deepDiffer';
import * as React from 'react';
import StyleSheet from '../../../exports/StyleSheet';
import View from '../../../exports/View';
import ScrollView from '../../../exports/ScrollView';
import VirtualizedList, { type RenderItemType } from '../VirtualizedList';

type ViewStyleProp = $PropertyType<ViewProps, 'style'>;
import invariant from 'fbjs/lib/invariant';
type $FlowFixMe = any;

type RequiredProps<ItemT> = {|
/**
Expand Down Expand Up @@ -103,7 +106,7 @@ type OptionalProps<ItemT> = {|
* 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,
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
Expand All @@ -120,18 +123,43 @@ type OptionalProps<ItemT> = {|
* and as the react key to track item re-ordering. The default extractor checks `item.key`, then
* falls back to using the index, like React does.
*/
keyExtractor: (item: ItemT, index: number) => string,
keyExtractor?: ?(item: ItemT, index: number) => string,
/**
* 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.
*
* The default value is 1.
*/
numColumns?: number,
/**
* Note: may have bugs (missing content) in some circumstances - use at your own risk.
*
* This may improve scroll performance for large lists.
*
* The default value is true for Android.
*/
numColumns: number,
removeClippedSubviews?: boolean,
/**
* See `ScrollView` for flow type and further documentation.
*/
fadingEdgeLength?: ?number,
|};

/**
* Default Props Helper Functions
* Use the following helper functions for default values
*/

// removeClippedSubviewsOrDefault(this.props.removeClippedSubviews)
function removeClippedSubviewsOrDefault(removeClippedSubviews: ?boolean) {
return removeClippedSubviews ?? Platform.OS === 'android';
}

// numColumnsOrDefault(this.props.numColumns)
function numColumnsOrDefault(numColumns: ?number) {
return numColumns ?? 1;
}

type FlatListProps<ItemT> = {|
...RequiredProps<ItemT>,
...OptionalProps<ItemT>,
Expand All @@ -155,12 +183,6 @@ export type Props<ItemT> = {
...
};

const defaultProps = {
...VirtualizedList.defaultProps,
numColumns: 1,
};
export type DefaultProps = typeof defaultProps;

/**
* A performant interface for rendering simple, flat lists, supporting the most handy features:
*
Expand Down Expand Up @@ -270,7 +292,6 @@ export type DefaultProps = typeof defaultProps;
* Also inherits [ScrollView Props](docs/scrollview.html#props), unless it is nested in another FlatList of same orientation.
*/
class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
static defaultProps: DefaultProps = defaultProps;
props: Props<ItemT>;
/**
* Scrolls to the end of the content. May be janky without `getItemLayout` prop.
Expand Down Expand Up @@ -354,7 +375,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
/**
* Provides a handle to the underlying scroll responder.
*/
getScrollResponder(): ?typeof ScrollView {
getScrollResponder(): ?ScrollResponderType {
if (this._listRef) {
return this._listRef.getScrollResponder();
}
Expand All @@ -365,7 +386,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
*/
getNativeScrollRef():
| ?React.ElementRef<typeof View>
| ?React.ElementRef<typeof ScrollView> {
| ?React.ElementRef<ScrollViewNativeComponent> {
if (this._listRef) {
/* $FlowFixMe[incompatible-return] Suppresses errors found when fixing
* TextInput typing */
Expand All @@ -389,19 +410,18 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
super(props);
this._checkProps(this.props);
if (this.props.viewabilityConfigCallbackPairs) {
this._virtualizedListPairs = this.props.viewabilityConfigCallbackPairs.map(
pair => ({
this._virtualizedListPairs =
this.props.viewabilityConfigCallbackPairs.map(pair => ({
viewabilityConfig: pair.viewabilityConfig,
onViewableItemsChanged: this._createOnViewableItemsChanged(
pair.onViewableItemsChanged,
),
}),
);
}));
} else if (this.props.onViewableItemsChanged) {
this._virtualizedListPairs.push({
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.63 was deployed. To see the error delete
* this comment and run Flow. */
/* $FlowFixMe[incompatible-call] (>=0.63.0 site=react_native_fb) This
* comment suppresses an error found when Flow v0.63 was deployed. To
* see the error delete this comment and run Flow. */
viewabilityConfig: this.props.viewabilityConfig,
onViewableItemsChanged: this._createOnViewableItemsChanged(
this.props.onViewableItemsChanged,
Expand Down Expand Up @@ -442,16 +462,16 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {

_checkProps(props: Props<ItemT>) {
const {
// $FlowFixMe this prop doesn't exist, is only used for an invariant
// $FlowFixMe[prop-missing] this prop doesn't exist, is only used for an invariant
getItem,
// $FlowFixMe this prop doesn't exist, is only used for an invariant
// $FlowFixMe[prop-missing] this prop doesn't exist, is only used for an invariant
getItemCount,
horizontal,
numColumns,
columnWrapperStyle,
onViewableItemsChanged,
viewabilityConfigCallbackPairs,
} = props;
const numColumns = numColumnsOrDefault(this.props.numColumns);
invariant(
!getItem && !getItemCount,
'FlatList does not support custom data formats.',
Expand All @@ -472,7 +492,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
}

_getItem = (data: Array<ItemT>, index: number) => {
const {numColumns} = this.props;
const numColumns = numColumnsOrDefault(this.props.numColumns);
if (numColumns > 1) {
const ret = [];
for (let kk = 0; kk < numColumns; kk++) {
Expand All @@ -489,36 +509,41 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {

_getItemCount = (data: ?Array<ItemT>): number => {
if (data) {
const {numColumns} = this.props;
const numColumns = numColumnsOrDefault(this.props.numColumns);
return numColumns > 1 ? Math.ceil(data.length / numColumns) : data.length;
} else {
return 0;
}
};

_keyExtractor = (items: ItemT | Array<ItemT>, index: number) => {
const {keyExtractor, numColumns} = this.props;
const numColumns = numColumnsOrDefault(this.props.numColumns);
const keyExtractor = this.props.keyExtractor ?? defaultKeyExtractor;

if (numColumns > 1) {
invariant(
Array.isArray(items),
'FlatList: Encountered internal consistency error, expected each item to consist of an ' +
'array with 1-%s columns; instead, received a single item.',
numColumns,
);
return (
items
// $FlowFixMe[incompatible-call]
.map((it, kk) => keyExtractor(it, index * numColumns + kk))
.join(':')
);
if (Array.isArray(items)) {
return items
.map((item, kk) =>
keyExtractor(((item: $FlowFixMe): ItemT), index * numColumns + kk),
)
.join(':');
} else {
invariant(
Array.isArray(items),
'FlatList: Encountered internal consistency error, expected each item to consist of an ' +
'array with 1-%s columns; instead, received a single item.',
numColumns,
);
}
} else {
// $FlowFixMe Can't call keyExtractor with an array
// $FlowFixMe[incompatible-call] Can't call keyExtractor with an array
return keyExtractor(items, index);
}
};

_pushMultiColumnViewable(arr: Array<ViewToken>, v: ViewToken): void {
const {numColumns, keyExtractor} = this.props;
const numColumns = numColumnsOrDefault(this.props.numColumns);
const keyExtractor = this.props.keyExtractor ?? defaultKeyExtractor;
v.item.forEach((item, ii) => {
invariant(v.index != null, 'Missing index!');
const index = v.index * numColumns + ii;
Expand All @@ -538,7 +563,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
changed: Array<ViewToken>,
...
}) => {
const {numColumns} = this.props;
const numColumns = numColumnsOrDefault(this.props.numColumns);
if (onViewableItemsChanged) {
if (numColumns > 1) {
const changed = [];
Expand All @@ -556,20 +581,18 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
}

_renderer = () => {
const {
ListItemComponent,
renderItem,
numColumns,
columnWrapperStyle,
} = this.props;
const {ListItemComponent, renderItem, columnWrapperStyle} = this.props;
const numColumns = numColumnsOrDefault(this.props.numColumns);

let virtualizedListRenderKey = ListItemComponent
? 'ListItemComponent'
: 'renderItem';

const renderer = (props): React.Node => {
if (ListItemComponent) {
// $FlowFixMe Component isn't valid
// $FlowFixMe[not-a-component] Component isn't valid
// $FlowFixMe[incompatible-type-arg] Component isn't valid
// $FlowFixMe[incompatible-return] Component isn't valid
return <ListItemComponent {...props} />;
} else if (renderItem) {
// $FlowFixMe[incompatible-call]
Expand All @@ -580,9 +603,9 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
};

return {
/* $FlowFixMe(>=0.111.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.111 was deployed. To see the error, delete
* this comment and run Flow. */
/* $FlowFixMe[invalid-computed-prop] (>=0.111.0 site=react_native_fb)
* This comment suppresses an error found when Flow v0.111 was deployed.
* To see the error, delete this comment and run Flow. */
[virtualizedListRenderKey]: (info: RenderItemProps<ItemT>) => {
if (numColumns > 1) {
const {item, index} = info;
Expand Down Expand Up @@ -612,7 +635,12 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
};

render(): React.Node {
const {numColumns, columnWrapperStyle, ...restProps} = this.props;
const {
numColumns,
columnWrapperStyle,
removeClippedSubviews: _removeClippedSubviews,
...restProps
} = this.props;

return (
<VirtualizedList
Expand All @@ -622,6 +650,9 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
keyExtractor={this._keyExtractor}
ref={this._captureRef}
viewabilityConfigCallbackPairs={this._virtualizedListPairs}
removeClippedSubviews={removeClippedSubviewsOrDefault(
_removeClippedSubviews,
)}
{...this._renderer()}
/>
);
Expand Down
Loading