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,
|
||||
} from './VirtualizeUtils';
|
||||
import * as VirtualizedListInjection from './VirtualizedListInjection';
|
||||
import CellRenderer from './VirtualizedListCellRenderer';
|
||||
import * as React from 'react';
|
||||
import ChildListCollection from './ChildListCollection';
|
||||
|
||||
|
@ -879,7 +880,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
_averageCellLength = 0;
|
||||
_cellRefs: {[string]: null | CellRenderer} = {};
|
||||
_cellRefs: {[string]: null | CellRenderer<any>} = {};
|
||||
_fillRateHelper: FillRateHelper;
|
||||
_frames: {
|
||||
[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({
|
||||
verticallyInverted: {
|
||||
transform: [{scaleY: -1}],
|
||||
|
@ -1822,15 +1607,6 @@ const styles = StyleSheet.create({
|
|||
horizontallyInverted: {
|
||||
transform: [{scaleX: -1}],
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
rowReverse: {
|
||||
flexDirection: 'row-reverse',
|
||||
},
|
||||
columnReverse: {
|
||||
flexDirection: 'column-reverse',
|
||||
},
|
||||
debug: {
|
||||
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 {ViewStyleProp} from '../StyleSheet/StyleSheet';
|
||||
import type {
|
||||
FocusEvent,
|
||||
LayoutEvent,
|
||||
ScrollEvent,
|
||||
} from '../Types/CoreEventTypes';
|
||||
import type {LayoutEvent, ScrollEvent} from '../Types/CoreEventTypes';
|
||||
|
||||
import type {ViewToken} from './ViewabilityHelper';
|
||||
import type {
|
||||
|
@ -40,9 +36,10 @@ import {
|
|||
import * as React from 'react';
|
||||
|
||||
import {CellRenderMask} from './CellRenderMask';
|
||||
import ChildListCollection from './ChildListCollection';
|
||||
import clamp from '../Utilities/clamp';
|
||||
import StateSafePureComponent from './StateSafePureComponent';
|
||||
import ChildListCollection from './ChildListCollection';
|
||||
import CellRenderer from './VirtualizedListCellRenderer';
|
||||
|
||||
const RefreshControl = require('../Components/RefreshControl/RefreshControl');
|
||||
const ScrollView = require('../Components/ScrollView/ScrollView');
|
||||
|
@ -728,7 +725,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|||
prevCellKey={prevCellKey}
|
||||
onCellLayout={this._onCellLayout}
|
||||
onUpdateSeparators={this._onUpdateSeparators}
|
||||
onFocusCapture={e => this._onCellFocusCapture(key)}
|
||||
onCellFocusCapture={e => this._onCellFocusCapture(key)}
|
||||
onUnmount={this._onCellUnmount}
|
||||
ref={ref => {
|
||||
this._cellRefs[key] = ref;
|
||||
|
@ -1080,7 +1077,7 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
|
|||
}
|
||||
|
||||
_averageCellLength = 0;
|
||||
_cellRefs: {[string]: null | CellRenderer} = {};
|
||||
_cellRefs: {[string]: null | CellRenderer<any>} = {};
|
||||
_fillRateHelper: FillRateHelper;
|
||||
_frames: {
|
||||
[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({
|
||||
verticallyInverted: {
|
||||
transform: [{scaleY: -1}],
|
||||
|
@ -2073,15 +1847,6 @@ const styles = StyleSheet.create({
|
|||
horizontallyInverted: {
|
||||
transform: [{scaleX: -1}],
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
rowReverse: {
|
||||
flexDirection: 'row-reverse',
|
||||
},
|
||||
columnReverse: {
|
||||
flexDirection: 'column-reverse',
|
||||
},
|
||||
debug: {
|
||||
flex: 1,
|
||||
},
|
||||
|
|
Загрузка…
Ссылка в новой задаче