Back out "Update typed export and fix test", Back out "[VirtualizedSectionList] Remove defaultProps", Back out "[VirtualizedList] Remove keyExtractor defaultProps"

Summary:
Changelog:
Partial revert the stack

Reviewed By: makovkastar

Differential Revision: D27156625

fbshipit-source-id: b158c9102047bb64ce708b200a8e5786f5a0c176
This commit is contained in:
Jimmy Zhang
2021-03-18 07:42:41 -07:00
committed by Facebook GitHub Bot
parent 729c6d2f41
commit f293d41ead
8 changed files with 104 additions and 255 deletions
+16 -21
View File
@@ -25,7 +25,6 @@ import type {
ViewabilityConfigCallbackPair,
} from './ViewabilityHelper';
import type {RenderItemType, RenderItemProps} from './VirtualizedList';
import {keyExtractor as defaultKeyExtractor} from './VirtualizeUtils';
type RequiredProps<ItemT> = {|
/**
@@ -121,7 +120,7 @@ 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.
@@ -157,6 +156,7 @@ export type Props<ItemT> = {
};
const defaultProps = {
...VirtualizedList.defaultProps,
numColumns: 1,
/**
* Enabling this prop on Android greatly improves scrolling performance with no known issues.
@@ -503,24 +503,20 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
};
_keyExtractor = (items: ItemT | Array<ItemT>, index: number) => {
const {numColumns} = this.props;
const keyExtractor = this.props.keyExtractor ?? defaultKeyExtractor;
const {keyExtractor, numColumns} = this.props;
if (numColumns > 1) {
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,
);
}
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(':')
);
} else {
// $FlowFixMe Can't call keyExtractor with an array
return keyExtractor(items, index);
@@ -528,8 +524,7 @@ class FlatList<ItemT> extends React.PureComponent<Props<ItemT>, void> {
};
_pushMultiColumnViewable(arr: Array<ViewToken>, v: ViewToken): void {
const {numColumns} = this.props;
const keyExtractor = this.props.keyExtractor ?? defaultKeyExtractor;
const {numColumns, keyExtractor} = this.props;
v.item.forEach((item, ii) => {
invariant(v.index != null, 'Missing index!');
const index = v.index * numColumns + ii;
+2 -5
View File
@@ -78,7 +78,7 @@ type OptionalProps<SectionT: SectionBase<any>> = {|
* falls back to using the index, like react does. Note that this sets keys for each item, but
* each overall section still needs its own key.
*/
keyExtractor?: ?(item: Item, index: number) => string,
keyExtractor: (item: Item, index: number) => string,
/**
* Called once when the scroll position gets within `onEndReachedThreshold` of the rendered
* content.
@@ -105,10 +105,6 @@ export type Props<SectionT> = {|
VirtualizedSectionListProps<SectionT>,
'renderItem',
>,
keyExtractor: $PropertyType<
VirtualizedSectionListProps<SectionT>,
'keyExtractor',
>,
...
},
>,
@@ -117,6 +113,7 @@ export type Props<SectionT> = {|
|};
const defaultProps = {
...VirtualizedSectionList.defaultProps,
stickySectionHeadersEnabled: Platform.OS === 'ios',
};
-10
View File
@@ -233,13 +233,3 @@ export function computeWindowedRenderLimits(
}
return {first, last};
}
export function keyExtractor(item: any, index: number): string {
if (typeof item === 'object' && item?.key != null) {
return item.key;
}
if (typeof item === 'object' && item?.id != null) {
return item.id;
}
return String(index);
}
+34 -37
View File
@@ -22,10 +22,7 @@ const infoLog = require('../Utilities/infoLog');
const invariant = require('invariant');
import VirtualizedListInjection from './VirtualizedListInjection';
import {
keyExtractor as defaultKeyExtractor,
computeWindowedRenderLimits,
} from './VirtualizeUtils';
import {computeWindowedRenderLimits} from './VirtualizeUtils';
import * as React from 'react';
import type {ScrollResponderType} from '../Components/ScrollView/ScrollView';
@@ -135,7 +132,7 @@ type OptionalProps = {|
* Reverses the direction of scroll. Uses scale transforms of -1.
*/
inverted?: ?boolean,
keyExtractor?: ?(item: Item, index: number) => string,
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.
@@ -306,6 +303,10 @@ type Props = {|
...OptionalProps,
|};
type DefaultProps = {|
keyExtractor: (item: Item, index: number) => string,
|};
let _usedIndexForKey = false;
let _keylessItemComponentName: string = '';
@@ -314,37 +315,26 @@ type State = {
last: number,
};
/**
* Default Props Helper Functions
* Use the following helper functions for default values
*/
// horizontalOrDefault(this.props.horizontal)
function horizontalOrDefault(horizontal: ?boolean) {
return horizontal ?? false;
}
// initialNumToRenderOrDefault(this.props.initialNumToRenderOrDefault)
function initialNumToRenderOrDefault(initialNumToRender: ?number) {
return initialNumToRender ?? 10;
}
// maxToRenderPerBatchOrDefault(this.props.maxToRenderPerBatch)
function maxToRenderPerBatchOrDefault(maxToRenderPerBatch: ?number) {
return maxToRenderPerBatch ?? 10;
}
// onEndReachedThresholdOrDefault(this.props.onEndReachedThreshold)
function onEndReachedThresholdOrDefault(onEndReachedThreshold: ?number) {
return onEndReachedThreshold ?? 2;
}
// scrollEventThrottleOrDefault(this.props.scrollEventThrottle)
function scrollEventThrottleOrDefault(scrollEventThrottle: ?number) {
return scrollEventThrottle ?? 50;
}
// windowSizeOrDefault(this.props.windowSize)
function windowSizeOrDefault(windowSize: ?number) {
return windowSize ?? 21;
}
@@ -375,7 +365,6 @@ function windowSizeOrDefault(windowSize: ?number) {
* 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
*
*/
class VirtualizedList extends React.PureComponent<Props, State> {
@@ -591,6 +580,22 @@ class VirtualizedList extends React.PureComponent<Props, State> {
}
}
static defaultProps: DefaultProps = {
keyExtractor: (item: Item, index: number) => {
if (item.key != null) {
return item.key;
}
if (item.id != null) {
return item.id;
}
_usedIndexForKey = true;
if (item.type && item.type.displayName) {
_keylessItemComponentName = item.type.displayName;
}
return String(index);
},
};
_getCellKey(): string {
return this.context?.cellKey || 'rootList';
}
@@ -799,6 +804,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
getItem,
getItemCount,
horizontal,
keyExtractor,
} = this.props;
const stickyOffset = this.props.ListHeaderComponent ? 1 : 0;
const end = getItemCount(data) - 1;
@@ -806,7 +812,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
last = Math.min(end, last);
for (let ii = first; ii <= last; ii++) {
const item = getItem(data, ii);
const key = this._keyExtractor(item, ii);
const key = keyExtractor(item, ii);
this._indicesToKeys.set(ii, key);
if (stickyIndicesFromProps.has(ii + stickyOffset)) {
stickyHeaderIndices.push(cells.length);
@@ -858,21 +864,6 @@ class VirtualizedList extends React.PureComponent<Props, State> {
_getSpacerKey = (isVertical: boolean): string =>
isVertical ? 'height' : 'width';
_keyExtractor(item: Item, index: number) {
if (this.props.keyExtractor != null) {
return this.props.keyExtractor(item, index);
}
const key = defaultKeyExtractor(item, index);
if (key === String(index)) {
_usedIndexForKey = true;
if (item.type && item.type.displayName) {
_keylessItemComponentName = item.type.displayName;
}
}
return key;
}
render(): React.Node {
if (__DEV__) {
const flatStyles = flattenStyle(this.props.contentContainerStyle);
@@ -1825,9 +1816,9 @@ class VirtualizedList extends React.PureComponent<Props, State> {
};
_createViewToken = (index: number, isViewable: boolean) => {
const {data, getItem} = this.props;
const {data, getItem, keyExtractor} = this.props;
const item = getItem(data, index);
return {index, item, key: this._keyExtractor(item, index), isViewable};
return {index, item, key: keyExtractor(item, index), isViewable};
};
_getFrameMetricsApprox = (
@@ -1863,13 +1854,19 @@ class VirtualizedList extends React.PureComponent<Props, State> {
inLayout?: boolean,
...
} => {
const {data, getItem, getItemCount, getItemLayout} = this.props;
const {
data,
getItem,
getItemCount,
getItemLayout,
keyExtractor,
} = this.props;
invariant(
getItemCount(data) > index,
'Tried to get frame for out of range index ' + index,
);
const item = getItem(data, index);
let frame = item && this._frames[this._keyExtractor(item, index)];
let frame = item && this._frames[keyExtractor(item, index)];
if (!frame || frame.index !== index) {
if (getItemLayout) {
frame = getItemLayout(data, index);
+17 -20
View File
@@ -17,7 +17,6 @@ const VirtualizedList = require('./VirtualizedList');
const invariant = require('invariant');
import type {ViewToken} from './ViewabilityHelper';
import {keyExtractor as defaultKeyExtractor} from './VirtualizeUtils';
type Item = any;
@@ -100,18 +99,14 @@ type OptionalProps<SectionT: SectionBase<any>> = {|
onEndReached?: ?({distanceFromEnd: number, ...}) => void,
|};
type VirtualizedListProps = React.ElementConfig<typeof VirtualizedList>;
type VirtualizedListProps = React.ElementProps<typeof VirtualizedList>;
export type Props<SectionT> = {|
...RequiredProps<SectionT>,
...OptionalProps<SectionT>,
...$Diff<
VirtualizedListProps,
{
renderItem: $PropertyType<VirtualizedListProps, 'renderItem'>,
data: $PropertyType<VirtualizedListProps, 'data'>,
...
},
{renderItem: $PropertyType<VirtualizedListProps, 'renderItem'>, ...},
>,
|};
export type ScrollToLocationParamsType = {|
@@ -122,6 +117,11 @@ export type ScrollToLocationParamsType = {|
viewPosition?: number,
|};
type DefaultProps = {|
...typeof VirtualizedList.defaultProps,
data: $ReadOnlyArray<Item>,
|};
type State = {childProps: VirtualizedListProps, ...};
/**
@@ -132,6 +132,11 @@ type State = {childProps: VirtualizedListProps, ...};
class VirtualizedSectionList<
SectionT: SectionBase<any>,
> extends React.PureComponent<Props<SectionT>, State> {
static defaultProps: DefaultProps = {
...VirtualizedList.defaultProps,
data: [],
};
scrollToLocation(params: ScrollToLocationParamsType) {
let index = params.itemIndex;
for (let i = 0; i < params.sectionIndex; i++) {
@@ -212,11 +217,11 @@ class VirtualizedSectionList<
);
}
_getItem(
_getItem = (
props: Props<SectionT>,
sections: ?$ReadOnlyArray<Item>,
index: number,
): ?Item {
): ?Item => {
if (!sections) {
return null;
}
@@ -238,7 +243,7 @@ class VirtualizedSectionList<
}
}
return null;
}
};
_keyExtractor = (item: Item, index: number) => {
const info = this._subExtractor(index);
@@ -287,8 +292,7 @@ class VirtualizedSectionList<
trailingSection: sections[i + 1],
};
} else {
const extractor =
section.keyExtractor || keyExtractor || defaultKeyExtractor;
const extractor = section.keyExtractor || keyExtractor;
return {
section,
key:
@@ -577,11 +581,4 @@ class ItemWithSeparator extends React.Component<
}
}
module.exports = (VirtualizedSectionList: React.AbstractComponent<
React.ElementConfig<typeof VirtualizedSectionList>,
$ReadOnly<{
getListRef: () => ?React.ElementRef<typeof VirtualizedList>,
scrollToLocation: (params: ScrollToLocationParamsType) => void,
...
}>,
>);
module.exports = VirtualizedSectionList;
@@ -103,37 +103,26 @@ describe('VirtualizedSectionList', () => {
it('handles separators correctly', () => {
const infos = [];
let component;
ReactTestRenderer.act(() => {
component = ReactTestRenderer.create(
<VirtualizedSectionList
ItemSeparatorComponent={props => <separator {...props} />}
sections={[
{title: 's0', data: [{key: 'i0'}, {key: 'i1'}, {key: 'i2'}]},
]}
renderItem={info => {
infos.push(info);
return <item title={info.item.key} />;
}}
getItem={(data, key) => data[key]}
getItemCount={data => data.length}
/>,
);
});
const component = ReactTestRenderer.create(
<VirtualizedSectionList
ItemSeparatorComponent={props => <separator {...props} />}
sections={[
{title: 's0', data: [{key: 'i0'}, {key: 'i1'}, {key: 'i2'}]},
]}
renderItem={info => {
infos.push(info);
return <item title={info.item.key} />;
}}
getItem={(data, key) => data[key]}
getItemCount={data => data.length}
/>,
);
expect(component).toMatchSnapshot();
ReactTestRenderer.act(() => {
infos[1].separators.highlight();
});
infos[1].separators.highlight();
expect(component).toMatchSnapshot();
ReactTestRenderer.act(() => {
infos[2].separators.updateProps('leading', {press: true});
});
expect(component).toMatchSnapshot();
ReactTestRenderer.act(() => {
infos[1].separators.unhighlight();
});
infos[2].separators.updateProps('leading', {press: true});
expect(component).toMatchSnapshot();
infos[1].separators.unhighlight();
});
it('handles nested lists', () => {
@@ -44,6 +44,7 @@ exports[`VirtualizedList forwards correct stickyHeaderIndices when all in initia
getItemCount={[Function]}
getItemLayout={[Function]}
initialNumToRender={10}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -185,6 +186,7 @@ exports[`VirtualizedList forwards correct stickyHeaderIndices when partially in
getItemCount={[Function]}
getItemLayout={[Function]}
initialNumToRender={5}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -264,6 +266,7 @@ exports[`VirtualizedList handles nested lists 1`] = `
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -294,6 +297,7 @@ exports[`VirtualizedList handles nested lists 1`] = `
getItem={[Function]}
getItemCount={[Function]}
horizontal={false}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -341,6 +345,7 @@ exports[`VirtualizedList handles nested lists 1`] = `
getItem={[Function]}
getItemCount={[Function]}
horizontal={true}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -408,6 +413,7 @@ exports[`VirtualizedList handles separators correctly 1`] = `
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -482,6 +488,7 @@ exports[`VirtualizedList handles separators correctly 2`] = `
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -556,6 +563,7 @@ exports[`VirtualizedList handles separators correctly 3`] = `
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -703,6 +711,7 @@ exports[`VirtualizedList keeps sticky headers realized after scrolled out of vie
getItemCount={[Function]}
getItemLayout={[Function]}
initialNumToRender={1}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -891,6 +900,7 @@ exports[`VirtualizedList realizes sticky headers in viewport on batched render 1
getItemCount={[Function]}
getItemLayout={[Function]}
initialNumToRender={1}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -1162,6 +1172,7 @@ exports[`VirtualizedList renders empty list 1`] = `
data={Array []}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -1185,6 +1196,7 @@ exports[`VirtualizedList renders empty list with empty component 1`] = `
data={Array []}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -1224,6 +1236,7 @@ exports[`VirtualizedList renders list with empty component 1`] = `
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -1252,6 +1265,7 @@ exports[`VirtualizedList renders null list 1`] = `
<RCTScrollView
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -1284,6 +1298,7 @@ exports[`VirtualizedList renders simple list 1`] = `
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -1342,6 +1357,7 @@ exports[`VirtualizedList renders simple list using ListItemComponent 1`] = `
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -1392,6 +1408,7 @@ exports[`VirtualizedList test getItem functionality where data is not an Array 1
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -1428,6 +1445,7 @@ exports[`VirtualizedList warns if both renderItem or ListItemComponent are speci
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
@@ -596,140 +596,6 @@ exports[`VirtualizedSectionList handles separators correctly 3`] = `
</RCTScrollView>
`;
exports[`VirtualizedSectionList handles separators correctly 4`] = `
<RCTScrollView
data={
Array [
Object {
"data": Array [
Object {
"key": "i0",
},
Object {
"key": "i1",
},
Object {
"key": "i2",
},
],
"title": "s0",
},
]
}
getItem={[Function]}
getItemCount={[Function]}
keyExtractor={[Function]}
onContentSizeChange={[Function]}
onLayout={[Function]}
onMomentumScrollBegin={[Function]}
onMomentumScrollEnd={[Function]}
onScroll={[Function]}
onScrollBeginDrag={[Function]}
onScrollEndDrag={[Function]}
renderItem={[Function]}
scrollEventThrottle={50}
stickyHeaderIndices={Array []}
>
<View>
<View
onLayout={[Function]}
style={null}
/>
<View
onLayout={[Function]}
style={null}
>
<View>
<item
title="i0"
/>
<separator
highlighted={false}
leadingItem={
Object {
"key": "i0",
}
}
section={
Object {
"data": Array [
Object {
"key": "i0",
},
Object {
"key": "i1",
},
Object {
"key": "i2",
},
],
"title": "s0",
}
}
trailingItem={
Object {
"key": "i1",
}
}
/>
</View>
</View>
<View
onLayout={[Function]}
style={null}
>
<View>
<item
title="i1"
/>
<separator
highlighted={false}
leadingItem={
Object {
"key": "i1",
}
}
press={true}
section={
Object {
"data": Array [
Object {
"key": "i0",
},
Object {
"key": "i1",
},
Object {
"key": "i2",
},
],
"title": "s0",
}
}
trailingItem={
Object {
"key": "i2",
}
}
/>
</View>
</View>
<View
onLayout={[Function]}
style={null}
>
<item
title="i2"
/>
</View>
<View
onLayout={[Function]}
style={null}
/>
</View>
</RCTScrollView>
`;
exports[`VirtualizedSectionList renders all the bells and whistles 1`] = `
<RCTScrollView
ListEmptyComponent={[Function]}