Extract VirtualizedListCellRenderer
Summary: VirtualizedList is large and complicated, getting larger and more complicated. This splits out a subcomponent, the cell renderer into its own file, since it is relatively isolated already. This uses the copy from VirtualizedList_EXPERIMENTAL, whose only real difference is exposing focus capture events to the containing VirtualizedList. Changelog: [Internal][Changed] - Extract VirtualizedListCellRenderer Reviewed By: rshest Differential Revision: D39648087 fbshipit-source-id: bb7c2eff0c658713c256650596f86e8788019baf
This commit is contained in:
Родитель
bc5cb7cd79
Коммит
8f0975a3b7
|
@ -33,6 +33,7 @@ import {
|
||||||
keyExtractor as defaultKeyExtractor,
|
keyExtractor as defaultKeyExtractor,
|
||||||
} from './VirtualizeUtils';
|
} from './VirtualizeUtils';
|
||||||
import * as VirtualizedListInjection from './VirtualizedListInjection';
|
import * as VirtualizedListInjection from './VirtualizedListInjection';
|
||||||
|
import CellRenderer from './VirtualizedListCellRenderer';
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import ChildListCollection from './ChildListCollection';
|
import ChildListCollection from './ChildListCollection';
|
||||||
|
|
||||||
|
@ -879,7 +880,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
_averageCellLength = 0;
|
_averageCellLength = 0;
|
||||||
_cellRefs: {[string]: null | CellRenderer} = {};
|
_cellRefs: {[string]: null | CellRenderer<any>} = {};
|
||||||
_fillRateHelper: FillRateHelper;
|
_fillRateHelper: FillRateHelper;
|
||||||
_frames: {
|
_frames: {
|
||||||
[string]: {
|
[string]: {
|
||||||
|
@ -1599,222 +1600,6 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type CellRendererProps = {
|
|
||||||
CellRendererComponent?: ?React.ComponentType<any>,
|
|
||||||
ItemSeparatorComponent: ?React.ComponentType<
|
|
||||||
any | {highlighted: boolean, leadingItem: ?Item},
|
|
||||||
>,
|
|
||||||
ListItemComponent?: ?(React.ComponentType<any> | React.Element<any>),
|
|
||||||
cellKey: string,
|
|
||||||
debug?: ?boolean,
|
|
||||||
fillRateHelper: FillRateHelper,
|
|
||||||
getItemLayout?: (
|
|
||||||
data: any,
|
|
||||||
index: number,
|
|
||||||
) => {
|
|
||||||
length: number,
|
|
||||||
offset: number,
|
|
||||||
index: number,
|
|
||||||
...
|
|
||||||
},
|
|
||||||
horizontal: ?boolean,
|
|
||||||
index: number,
|
|
||||||
inversionStyle: ViewStyleProp,
|
|
||||||
item: Item,
|
|
||||||
// This is extracted by ScrollViewStickyHeader
|
|
||||||
onCellLayout: (event: Object, cellKey: string, index: number) => void,
|
|
||||||
onUnmount: (cellKey: string) => void,
|
|
||||||
onUpdateSeparators: (cellKeys: Array<?string>, props: Object) => void,
|
|
||||||
prevCellKey: ?string,
|
|
||||||
renderItem?: ?RenderItemType<Item>,
|
|
||||||
...
|
|
||||||
};
|
|
||||||
|
|
||||||
type CellRendererState = {
|
|
||||||
separatorProps: $ReadOnly<{|
|
|
||||||
highlighted: boolean,
|
|
||||||
leadingItem: ?Item,
|
|
||||||
|}>,
|
|
||||||
...
|
|
||||||
};
|
|
||||||
|
|
||||||
class CellRenderer extends React.Component<
|
|
||||||
CellRendererProps,
|
|
||||||
CellRendererState,
|
|
||||||
> {
|
|
||||||
// $FlowFixMe[missing-local-annot]
|
|
||||||
state = {
|
|
||||||
separatorProps: {
|
|
||||||
highlighted: false,
|
|
||||||
leadingItem: this.props.item,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static getDerivedStateFromProps(
|
|
||||||
props: CellRendererProps,
|
|
||||||
prevState: CellRendererState,
|
|
||||||
): ?CellRendererState {
|
|
||||||
return {
|
|
||||||
separatorProps: {
|
|
||||||
...prevState.separatorProps,
|
|
||||||
leadingItem: props.item,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: consider factoring separator stuff out of VirtualizedList into FlatList since it's not
|
|
||||||
// reused by SectionList and we can keep VirtualizedList simpler.
|
|
||||||
// $FlowFixMe[missing-local-annot]
|
|
||||||
_separators = {
|
|
||||||
highlight: () => {
|
|
||||||
const {cellKey, prevCellKey} = this.props;
|
|
||||||
this.props.onUpdateSeparators([cellKey, prevCellKey], {
|
|
||||||
highlighted: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
unhighlight: () => {
|
|
||||||
const {cellKey, prevCellKey} = this.props;
|
|
||||||
this.props.onUpdateSeparators([cellKey, prevCellKey], {
|
|
||||||
highlighted: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
updateProps: (select: 'leading' | 'trailing', newProps: Object) => {
|
|
||||||
const {cellKey, prevCellKey} = this.props;
|
|
||||||
this.props.onUpdateSeparators(
|
|
||||||
[select === 'leading' ? prevCellKey : cellKey],
|
|
||||||
newProps,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
updateSeparatorProps(newProps: Object) {
|
|
||||||
this.setState(state => ({
|
|
||||||
separatorProps: {...state.separatorProps, ...newProps},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.props.onUnmount(this.props.cellKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onLayout = (nativeEvent: LayoutEvent): void => {
|
|
||||||
this.props.onCellLayout &&
|
|
||||||
this.props.onCellLayout(
|
|
||||||
nativeEvent,
|
|
||||||
this.props.cellKey,
|
|
||||||
this.props.index,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
_renderElement(
|
|
||||||
renderItem: any,
|
|
||||||
ListItemComponent: any,
|
|
||||||
item: any,
|
|
||||||
index: any,
|
|
||||||
// $FlowFixMe[missing-local-annot]
|
|
||||||
) {
|
|
||||||
if (renderItem && ListItemComponent) {
|
|
||||||
console.warn(
|
|
||||||
'VirtualizedList: Both ListItemComponent and renderItem props are present. ListItemComponent will take' +
|
|
||||||
' precedence over renderItem.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ListItemComponent) {
|
|
||||||
/* $FlowFixMe[not-a-component] (>=0.108.0 site=react_native_fb) This
|
|
||||||
* comment suppresses an error found when Flow v0.108 was deployed. To
|
|
||||||
* see the error, delete this comment and run Flow. */
|
|
||||||
/* $FlowFixMe[incompatible-type-arg] (>=0.108.0 site=react_native_fb)
|
|
||||||
* This comment suppresses an error found when Flow v0.108 was deployed.
|
|
||||||
* To see the error, delete this comment and run Flow. */
|
|
||||||
return React.createElement(ListItemComponent, {
|
|
||||||
item,
|
|
||||||
index,
|
|
||||||
separators: this._separators,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (renderItem) {
|
|
||||||
return renderItem({
|
|
||||||
item,
|
|
||||||
index,
|
|
||||||
separators: this._separators,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
invariant(
|
|
||||||
false,
|
|
||||||
'VirtualizedList: Either ListItemComponent or renderItem props are required but none were found.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// $FlowFixMe[missing-local-annot]
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
CellRendererComponent,
|
|
||||||
ItemSeparatorComponent,
|
|
||||||
ListItemComponent,
|
|
||||||
debug,
|
|
||||||
fillRateHelper,
|
|
||||||
getItemLayout,
|
|
||||||
horizontal,
|
|
||||||
item,
|
|
||||||
index,
|
|
||||||
inversionStyle,
|
|
||||||
renderItem,
|
|
||||||
} = this.props;
|
|
||||||
const element = this._renderElement(
|
|
||||||
renderItem,
|
|
||||||
ListItemComponent,
|
|
||||||
item,
|
|
||||||
index,
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLayout =
|
|
||||||
(getItemLayout && !debug && !fillRateHelper.enabled()) ||
|
|
||||||
!this.props.onCellLayout
|
|
||||||
? undefined
|
|
||||||
: this._onLayout;
|
|
||||||
// NOTE: that when this is a sticky header, `onLayout` will get automatically extracted and
|
|
||||||
// called explicitly by `ScrollViewStickyHeader`.
|
|
||||||
const itemSeparator = React.isValidElement(ItemSeparatorComponent)
|
|
||||||
? ItemSeparatorComponent
|
|
||||||
: ItemSeparatorComponent && (
|
|
||||||
<ItemSeparatorComponent {...this.state.separatorProps} />
|
|
||||||
);
|
|
||||||
const cellStyle = inversionStyle
|
|
||||||
? horizontal
|
|
||||||
? [styles.rowReverse, inversionStyle]
|
|
||||||
: [styles.columnReverse, inversionStyle]
|
|
||||||
: horizontal
|
|
||||||
? [styles.row, inversionStyle]
|
|
||||||
: inversionStyle;
|
|
||||||
const result = !CellRendererComponent ? (
|
|
||||||
/* $FlowFixMe[incompatible-type-arg] (>=0.89.0 site=react_native_fb) *
|
|
||||||
This comment suppresses an error found when Flow v0.89 was deployed. *
|
|
||||||
To see the error, delete this comment and run Flow. */
|
|
||||||
<View style={cellStyle} onLayout={onLayout}>
|
|
||||||
{element}
|
|
||||||
{itemSeparator}
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<CellRendererComponent
|
|
||||||
{...this.props}
|
|
||||||
style={cellStyle}
|
|
||||||
onLayout={onLayout}>
|
|
||||||
{element}
|
|
||||||
{itemSeparator}
|
|
||||||
</CellRendererComponent>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<VirtualizedListCellContextProvider cellKey={this.props.cellKey}>
|
|
||||||
{result}
|
|
||||||
</VirtualizedListCellContextProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
verticallyInverted: {
|
verticallyInverted: {
|
||||||
transform: [{scaleY: -1}],
|
transform: [{scaleY: -1}],
|
||||||
|
@ -1822,15 +1607,6 @@ const styles = StyleSheet.create({
|
||||||
horizontallyInverted: {
|
horizontallyInverted: {
|
||||||
transform: [{scaleX: -1}],
|
transform: [{scaleX: -1}],
|
||||||
},
|
},
|
||||||
row: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
},
|
|
||||||
rowReverse: {
|
|
||||||
flexDirection: 'row-reverse',
|
|
||||||
},
|
|
||||||
columnReverse: {
|
|
||||||
flexDirection: 'column-reverse',
|
|
||||||
},
|
|
||||||
debug: {
|
debug: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,261 @@
|
||||||
|
/**
|
||||||
|
* 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 * as React from 'react';
|
||||||
|
|
||||||
|
import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
|
||||||
|
import type {FocusEvent, LayoutEvent} from '../Types/CoreEventTypes';
|
||||||
|
import type {RenderItemType} from './VirtualizedListProps';
|
||||||
|
import type FillRateHelper from './FillRateHelper';
|
||||||
|
|
||||||
|
import {VirtualizedListCellContextProvider} from './VirtualizedListContext.js';
|
||||||
|
import View from '../Components/View/View';
|
||||||
|
import StyleSheet from '../StyleSheet/StyleSheet';
|
||||||
|
|
||||||
|
import invariant from 'invariant';
|
||||||
|
|
||||||
|
export type Props<ItemT> = {
|
||||||
|
CellRendererComponent?: ?React.ComponentType<any>,
|
||||||
|
ItemSeparatorComponent: ?React.ComponentType<
|
||||||
|
any | {highlighted: boolean, leadingItem: ?ItemT},
|
||||||
|
>,
|
||||||
|
ListItemComponent?: ?(React.ComponentType<any> | React.Element<any>),
|
||||||
|
cellKey: string,
|
||||||
|
debug?: ?boolean,
|
||||||
|
fillRateHelper: FillRateHelper,
|
||||||
|
getItemLayout?: (
|
||||||
|
data: any,
|
||||||
|
index: number,
|
||||||
|
) => {
|
||||||
|
length: number,
|
||||||
|
offset: number,
|
||||||
|
index: number,
|
||||||
|
...
|
||||||
|
},
|
||||||
|
horizontal: ?boolean,
|
||||||
|
index: number,
|
||||||
|
inversionStyle: ViewStyleProp,
|
||||||
|
item: ItemT,
|
||||||
|
onCellLayout: (event: LayoutEvent, cellKey: string, index: number) => void,
|
||||||
|
onCellFocusCapture?: (event: FocusEvent) => void,
|
||||||
|
onUnmount: (cellKey: string) => void,
|
||||||
|
onUpdateSeparators: (
|
||||||
|
cellKeys: Array<?string>,
|
||||||
|
props: $Shape<SeparatorProps<ItemT>>,
|
||||||
|
) => void,
|
||||||
|
prevCellKey: ?string,
|
||||||
|
renderItem?: ?RenderItemType<ItemT>,
|
||||||
|
...
|
||||||
|
};
|
||||||
|
|
||||||
|
type SeparatorProps<ItemT> = $ReadOnly<{|
|
||||||
|
highlighted: boolean,
|
||||||
|
leadingItem: ?ItemT,
|
||||||
|
|}>;
|
||||||
|
|
||||||
|
type State<ItemT> = {
|
||||||
|
separatorProps: SeparatorProps<ItemT>,
|
||||||
|
...
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class CellRenderer<ItemT> extends React.Component<
|
||||||
|
Props<ItemT>,
|
||||||
|
State<ItemT>,
|
||||||
|
> {
|
||||||
|
state: State<ItemT> = {
|
||||||
|
separatorProps: {
|
||||||
|
highlighted: false,
|
||||||
|
leadingItem: this.props.item,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static getDerivedStateFromProps(
|
||||||
|
props: Props<ItemT>,
|
||||||
|
prevState: State<ItemT>,
|
||||||
|
): ?State<ItemT> {
|
||||||
|
return {
|
||||||
|
separatorProps: {
|
||||||
|
...prevState.separatorProps,
|
||||||
|
leadingItem: props.item,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: consider factoring separator stuff out of VirtualizedList into FlatList since it's not
|
||||||
|
// reused by SectionList and we can keep VirtualizedList simpler.
|
||||||
|
// $FlowFixMe[missing-local-annot]
|
||||||
|
_separators = {
|
||||||
|
highlight: () => {
|
||||||
|
const {cellKey, prevCellKey} = this.props;
|
||||||
|
this.props.onUpdateSeparators([cellKey, prevCellKey], {
|
||||||
|
highlighted: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
unhighlight: () => {
|
||||||
|
const {cellKey, prevCellKey} = this.props;
|
||||||
|
this.props.onUpdateSeparators([cellKey, prevCellKey], {
|
||||||
|
highlighted: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
updateProps: (
|
||||||
|
select: 'leading' | 'trailing',
|
||||||
|
newProps: SeparatorProps<ItemT>,
|
||||||
|
) => {
|
||||||
|
const {cellKey, prevCellKey} = this.props;
|
||||||
|
this.props.onUpdateSeparators(
|
||||||
|
[select === 'leading' ? prevCellKey : cellKey],
|
||||||
|
newProps,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
updateSeparatorProps(newProps: SeparatorProps<ItemT>) {
|
||||||
|
this.setState(state => ({
|
||||||
|
separatorProps: {...state.separatorProps, ...newProps},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.props.onUnmount(this.props.cellKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onLayout = (nativeEvent: LayoutEvent): void => {
|
||||||
|
this.props.onCellLayout &&
|
||||||
|
this.props.onCellLayout(
|
||||||
|
nativeEvent,
|
||||||
|
this.props.cellKey,
|
||||||
|
this.props.index,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
_renderElement(
|
||||||
|
renderItem: ?RenderItemType<ItemT>,
|
||||||
|
ListItemComponent: any,
|
||||||
|
item: ItemT,
|
||||||
|
index: number,
|
||||||
|
): React.Node {
|
||||||
|
if (renderItem && ListItemComponent) {
|
||||||
|
console.warn(
|
||||||
|
'VirtualizedList: Both ListItemComponent and renderItem props are present. ListItemComponent will take' +
|
||||||
|
' precedence over renderItem.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ListItemComponent) {
|
||||||
|
/* $FlowFixMe[not-a-component] (>=0.108.0 site=react_native_fb) This
|
||||||
|
* comment suppresses an error found when Flow v0.108 was deployed. To
|
||||||
|
* see the error, delete this comment and run Flow. */
|
||||||
|
/* $FlowFixMe[incompatible-type-arg] (>=0.108.0 site=react_native_fb)
|
||||||
|
* This comment suppresses an error found when Flow v0.108 was deployed.
|
||||||
|
* To see the error, delete this comment and run Flow. */
|
||||||
|
return React.createElement(ListItemComponent, {
|
||||||
|
item,
|
||||||
|
index,
|
||||||
|
separators: this._separators,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderItem) {
|
||||||
|
return renderItem({
|
||||||
|
item,
|
||||||
|
index,
|
||||||
|
separators: this._separators,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
invariant(
|
||||||
|
false,
|
||||||
|
'VirtualizedList: Either ListItemComponent or renderItem props are required but none were found.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(): React.Node {
|
||||||
|
const {
|
||||||
|
CellRendererComponent,
|
||||||
|
ItemSeparatorComponent,
|
||||||
|
ListItemComponent,
|
||||||
|
debug,
|
||||||
|
fillRateHelper,
|
||||||
|
getItemLayout,
|
||||||
|
horizontal,
|
||||||
|
item,
|
||||||
|
index,
|
||||||
|
inversionStyle,
|
||||||
|
onCellFocusCapture,
|
||||||
|
renderItem,
|
||||||
|
} = this.props;
|
||||||
|
const element = this._renderElement(
|
||||||
|
renderItem,
|
||||||
|
ListItemComponent,
|
||||||
|
item,
|
||||||
|
index,
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLayout =
|
||||||
|
(getItemLayout && !debug && !fillRateHelper.enabled()) ||
|
||||||
|
!this.props.onCellLayout
|
||||||
|
? undefined
|
||||||
|
: this._onLayout;
|
||||||
|
// NOTE: that when this is a sticky header, `onLayout` will get automatically extracted and
|
||||||
|
// called explicitly by `ScrollViewStickyHeader`.
|
||||||
|
const itemSeparator = React.isValidElement(ItemSeparatorComponent)
|
||||||
|
? ItemSeparatorComponent
|
||||||
|
: ItemSeparatorComponent && (
|
||||||
|
<ItemSeparatorComponent {...this.state.separatorProps} />
|
||||||
|
);
|
||||||
|
const cellStyle = inversionStyle
|
||||||
|
? horizontal
|
||||||
|
? [styles.rowReverse, inversionStyle]
|
||||||
|
: [styles.columnReverse, inversionStyle]
|
||||||
|
: horizontal
|
||||||
|
? [styles.row, inversionStyle]
|
||||||
|
: inversionStyle;
|
||||||
|
const result = !CellRendererComponent ? (
|
||||||
|
<View
|
||||||
|
style={cellStyle}
|
||||||
|
onLayout={onLayout}
|
||||||
|
onFocusCapture={onCellFocusCapture}
|
||||||
|
/* $FlowFixMe[incompatible-type-arg] (>=0.89.0 site=react_native_fb) *
|
||||||
|
This comment suppresses an error found when Flow v0.89 was deployed. *
|
||||||
|
To see the error, delete this comment and run Flow. */
|
||||||
|
>
|
||||||
|
{element}
|
||||||
|
{itemSeparator}
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<CellRendererComponent
|
||||||
|
{...this.props}
|
||||||
|
style={cellStyle}
|
||||||
|
onLayout={onLayout}
|
||||||
|
onFocusCapture={onCellFocusCapture}>
|
||||||
|
{element}
|
||||||
|
{itemSeparator}
|
||||||
|
</CellRendererComponent>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VirtualizedListCellContextProvider cellKey={this.props.cellKey}>
|
||||||
|
{result}
|
||||||
|
</VirtualizedListCellContextProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
row: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
rowReverse: {
|
||||||
|
flexDirection: 'row-reverse',
|
||||||
|
},
|
||||||
|
columnReverse: {
|
||||||
|
flexDirection: 'column-reverse',
|
||||||
|
},
|
||||||
|
});
|
|
@ -10,11 +10,7 @@
|
||||||
|
|
||||||
import type {ScrollResponderType} from '../Components/ScrollView/ScrollView';
|
import type {ScrollResponderType} from '../Components/ScrollView/ScrollView';
|
||||||
import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
|
import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
|
||||||
import type {
|
import type {LayoutEvent, ScrollEvent} from '../Types/CoreEventTypes';
|
||||||
FocusEvent,
|
|
||||||
LayoutEvent,
|
|
||||||
ScrollEvent,
|
|
||||||
} from '../Types/CoreEventTypes';
|
|
||||||
|
|
||||||
import type {ViewToken} from './ViewabilityHelper';
|
import type {ViewToken} from './ViewabilityHelper';
|
||||||
import type {
|
import type {
|
||||||
|
@ -40,9 +36,10 @@ import {
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import {CellRenderMask} from './CellRenderMask';
|
import {CellRenderMask} from './CellRenderMask';
|
||||||
|
import ChildListCollection from './ChildListCollection';
|
||||||
import clamp from '../Utilities/clamp';
|
import clamp from '../Utilities/clamp';
|
||||||
import StateSafePureComponent from './StateSafePureComponent';
|
import StateSafePureComponent from './StateSafePureComponent';
|
||||||
import ChildListCollection from './ChildListCollection';
|
import CellRenderer from './VirtualizedListCellRenderer';
|
||||||
|
|
||||||
const RefreshControl = require('../Components/RefreshControl/RefreshControl');
|
const RefreshControl = require('../Components/RefreshControl/RefreshControl');
|
||||||
const ScrollView = require('../Components/ScrollView/ScrollView');
|
const ScrollView = require('../Components/ScrollView/ScrollView');
|
||||||
|
@ -728,7 +725,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
||||||
prevCellKey={prevCellKey}
|
prevCellKey={prevCellKey}
|
||||||
onCellLayout={this._onCellLayout}
|
onCellLayout={this._onCellLayout}
|
||||||
onUpdateSeparators={this._onUpdateSeparators}
|
onUpdateSeparators={this._onUpdateSeparators}
|
||||||
onFocusCapture={e => this._onCellFocusCapture(key)}
|
onCellFocusCapture={e => this._onCellFocusCapture(key)}
|
||||||
onUnmount={this._onCellUnmount}
|
onUnmount={this._onCellUnmount}
|
||||||
ref={ref => {
|
ref={ref => {
|
||||||
this._cellRefs[key] = ref;
|
this._cellRefs[key] = ref;
|
||||||
|
@ -1080,7 +1077,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
_averageCellLength = 0;
|
_averageCellLength = 0;
|
||||||
_cellRefs: {[string]: null | CellRenderer} = {};
|
_cellRefs: {[string]: null | CellRenderer<any>} = {};
|
||||||
_fillRateHelper: FillRateHelper;
|
_fillRateHelper: FillRateHelper;
|
||||||
_frames: {
|
_frames: {
|
||||||
[string]: {
|
[string]: {
|
||||||
|
@ -1843,229 +1840,6 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type CellRendererProps = {
|
|
||||||
CellRendererComponent?: ?React.ComponentType<any>,
|
|
||||||
ItemSeparatorComponent: ?React.ComponentType<
|
|
||||||
any | {highlighted: boolean, leadingItem: ?Item},
|
|
||||||
>,
|
|
||||||
ListItemComponent?: ?(React.ComponentType<any> | React.Element<any>),
|
|
||||||
cellKey: string,
|
|
||||||
debug?: ?boolean,
|
|
||||||
fillRateHelper: FillRateHelper,
|
|
||||||
getItemLayout?: (
|
|
||||||
data: any,
|
|
||||||
index: number,
|
|
||||||
) => {
|
|
||||||
length: number,
|
|
||||||
offset: number,
|
|
||||||
index: number,
|
|
||||||
...
|
|
||||||
},
|
|
||||||
horizontal: ?boolean,
|
|
||||||
index: number,
|
|
||||||
inversionStyle: ViewStyleProp,
|
|
||||||
item: Item,
|
|
||||||
// This is extracted by ScrollViewStickyHeader
|
|
||||||
onCellLayout: (event: Object, cellKey: string, index: number) => void,
|
|
||||||
onUnmount: (cellKey: string) => void,
|
|
||||||
onUpdateSeparators: (cellKeys: Array<?string>, props: Object) => void,
|
|
||||||
prevCellKey: ?string,
|
|
||||||
onFocusCapture: (event: FocusEvent) => void,
|
|
||||||
renderItem?: ?RenderItemType<Item>,
|
|
||||||
...
|
|
||||||
};
|
|
||||||
|
|
||||||
type CellRendererState = {
|
|
||||||
separatorProps: $ReadOnly<{|
|
|
||||||
highlighted: boolean,
|
|
||||||
leadingItem: ?Item,
|
|
||||||
|}>,
|
|
||||||
...
|
|
||||||
};
|
|
||||||
|
|
||||||
class CellRenderer extends React.Component<
|
|
||||||
CellRendererProps,
|
|
||||||
CellRendererState,
|
|
||||||
> {
|
|
||||||
// $FlowFixMe[missing-local-annot]
|
|
||||||
state = {
|
|
||||||
separatorProps: {
|
|
||||||
highlighted: false,
|
|
||||||
leadingItem: this.props.item,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static getDerivedStateFromProps(
|
|
||||||
props: CellRendererProps,
|
|
||||||
prevState: CellRendererState,
|
|
||||||
): ?CellRendererState {
|
|
||||||
return {
|
|
||||||
separatorProps: {
|
|
||||||
...prevState.separatorProps,
|
|
||||||
leadingItem: props.item,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: consider factoring separator stuff out of VirtualizedList into FlatList since it's not
|
|
||||||
// reused by SectionList and we can keep VirtualizedList simpler.
|
|
||||||
// $FlowFixMe[missing-local-annot]
|
|
||||||
_separators = {
|
|
||||||
highlight: () => {
|
|
||||||
const {cellKey, prevCellKey} = this.props;
|
|
||||||
this.props.onUpdateSeparators([cellKey, prevCellKey], {
|
|
||||||
highlighted: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
unhighlight: () => {
|
|
||||||
const {cellKey, prevCellKey} = this.props;
|
|
||||||
this.props.onUpdateSeparators([cellKey, prevCellKey], {
|
|
||||||
highlighted: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
updateProps: (select: 'leading' | 'trailing', newProps: Object) => {
|
|
||||||
const {cellKey, prevCellKey} = this.props;
|
|
||||||
this.props.onUpdateSeparators(
|
|
||||||
[select === 'leading' ? prevCellKey : cellKey],
|
|
||||||
newProps,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
updateSeparatorProps(newProps: Object) {
|
|
||||||
this.setState(state => ({
|
|
||||||
separatorProps: {...state.separatorProps, ...newProps},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.props.onUnmount(this.props.cellKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onLayout = (nativeEvent: LayoutEvent): void => {
|
|
||||||
this.props.onCellLayout &&
|
|
||||||
this.props.onCellLayout(
|
|
||||||
nativeEvent,
|
|
||||||
this.props.cellKey,
|
|
||||||
this.props.index,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
_renderElement(
|
|
||||||
renderItem: any,
|
|
||||||
ListItemComponent: any,
|
|
||||||
item: any,
|
|
||||||
index: any,
|
|
||||||
// $FlowFixMe[missing-local-annot]
|
|
||||||
) {
|
|
||||||
if (renderItem && ListItemComponent) {
|
|
||||||
console.warn(
|
|
||||||
'VirtualizedList: Both ListItemComponent and renderItem props are present. ListItemComponent will take' +
|
|
||||||
' precedence over renderItem.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ListItemComponent) {
|
|
||||||
/* $FlowFixMe[not-a-component] (>=0.108.0 site=react_native_fb) This
|
|
||||||
* comment suppresses an error found when Flow v0.108 was deployed. To
|
|
||||||
* see the error, delete this comment and run Flow. */
|
|
||||||
/* $FlowFixMe[incompatible-type-arg] (>=0.108.0 site=react_native_fb)
|
|
||||||
* This comment suppresses an error found when Flow v0.108 was deployed.
|
|
||||||
* To see the error, delete this comment and run Flow. */
|
|
||||||
return React.createElement(ListItemComponent, {
|
|
||||||
item,
|
|
||||||
index,
|
|
||||||
separators: this._separators,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (renderItem) {
|
|
||||||
return renderItem({
|
|
||||||
item,
|
|
||||||
index,
|
|
||||||
separators: this._separators,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
invariant(
|
|
||||||
false,
|
|
||||||
'VirtualizedList: Either ListItemComponent or renderItem props are required but none were found.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// $FlowFixMe[missing-local-annot]
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
CellRendererComponent,
|
|
||||||
ItemSeparatorComponent,
|
|
||||||
ListItemComponent,
|
|
||||||
debug,
|
|
||||||
fillRateHelper,
|
|
||||||
getItemLayout,
|
|
||||||
horizontal,
|
|
||||||
item,
|
|
||||||
index,
|
|
||||||
inversionStyle,
|
|
||||||
onFocusCapture,
|
|
||||||
renderItem,
|
|
||||||
} = this.props;
|
|
||||||
const element = this._renderElement(
|
|
||||||
renderItem,
|
|
||||||
ListItemComponent,
|
|
||||||
item,
|
|
||||||
index,
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLayout =
|
|
||||||
(getItemLayout && !debug && !fillRateHelper.enabled()) ||
|
|
||||||
!this.props.onCellLayout
|
|
||||||
? undefined
|
|
||||||
: this._onLayout;
|
|
||||||
// NOTE: that when this is a sticky header, `onLayout` will get automatically extracted and
|
|
||||||
// called explicitly by `ScrollViewStickyHeader`.
|
|
||||||
const itemSeparator = React.isValidElement(ItemSeparatorComponent)
|
|
||||||
? ItemSeparatorComponent
|
|
||||||
: ItemSeparatorComponent && (
|
|
||||||
<ItemSeparatorComponent {...this.state.separatorProps} />
|
|
||||||
);
|
|
||||||
const cellStyle = inversionStyle
|
|
||||||
? horizontal
|
|
||||||
? [styles.rowReverse, inversionStyle]
|
|
||||||
: [styles.columnReverse, inversionStyle]
|
|
||||||
: horizontal
|
|
||||||
? [styles.row, inversionStyle]
|
|
||||||
: inversionStyle;
|
|
||||||
const result = !CellRendererComponent ? (
|
|
||||||
<View
|
|
||||||
style={cellStyle}
|
|
||||||
onLayout={onLayout}
|
|
||||||
onFocusCapture={onFocusCapture}
|
|
||||||
/* $FlowFixMe[incompatible-type-arg] (>=0.89.0 site=react_native_fb) *
|
|
||||||
This comment suppresses an error found when Flow v0.89 was deployed. *
|
|
||||||
To see the error, delete this comment and run Flow. */
|
|
||||||
>
|
|
||||||
{element}
|
|
||||||
{itemSeparator}
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<CellRendererComponent
|
|
||||||
{...this.props}
|
|
||||||
style={cellStyle}
|
|
||||||
onLayout={onLayout}
|
|
||||||
onFocusCapture={onFocusCapture}>
|
|
||||||
{element}
|
|
||||||
{itemSeparator}
|
|
||||||
</CellRendererComponent>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<VirtualizedListCellContextProvider cellKey={this.props.cellKey}>
|
|
||||||
{result}
|
|
||||||
</VirtualizedListCellContextProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
verticallyInverted: {
|
verticallyInverted: {
|
||||||
transform: [{scaleY: -1}],
|
transform: [{scaleY: -1}],
|
||||||
|
@ -2073,15 +1847,6 @@ const styles = StyleSheet.create({
|
||||||
horizontallyInverted: {
|
horizontallyInverted: {
|
||||||
transform: [{scaleX: -1}],
|
transform: [{scaleX: -1}],
|
||||||
},
|
},
|
||||||
row: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
},
|
|
||||||
rowReverse: {
|
|
||||||
flexDirection: 'row-reverse',
|
|
||||||
},
|
|
||||||
columnReverse: {
|
|
||||||
flexDirection: 'column-reverse',
|
|
||||||
},
|
|
||||||
debug: {
|
debug: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
|
|
Загрузка…
Ссылка в новой задаче