Fix types + documentation for CellRendererComponent

Summary:
CellRendererComponent can be given a more useful description, and more constrained type, to ensure it is used more correctly.

Changelog:
[General][Fixed] - Fix types + documentation for CellRendererComponent

Reviewed By: yungsters

Differential Revision: D43925572

fbshipit-source-id: 26aae6a2df989993c97709ffbf1544df7cbae036
This commit is contained in:
Nick Gerleman
2023-03-14 15:32:05 -07:00
committed by Facebook GitHub Bot
parent ccbbcaab9c
commit 2d41e6642e
5 changed files with 60 additions and 39 deletions
+20 -1
View File
@@ -87,6 +87,16 @@ export type ListRenderItem<ItemT> = (
info: ListRenderItemInfo<ItemT>,
) => React.ReactElement | null;
export interface CellRendererProps<ItemT> {
cellKey: string;
children: React.ReactNode;
index: number;
item: ItemT;
onFocusCapture?: ((event: FocusEvent) => void) | undefined;
onLayout?: ((event: LayoutChangeEvent) => void) | undefined;
style: StyleProp<ViewStyle> | undefined;
}
/**
* @see https://reactnative.dev/docs/virtualizedlist
*/
@@ -370,5 +380,14 @@ export interface VirtualizedListWithoutRenderItemProps<ItemT>
*/
windowSize?: number | undefined;
CellRendererComponent?: React.ComponentType<any> | undefined;
/**
* CellRendererComponent allows customizing how cells rendered by
* `renderItem`/`ListItemComponent` are wrapped when placed into the
* underlying ScrollView. This component must accept event handlers which
* notify VirtualizedList of changes within the cell.
*/
CellRendererComponent?:
| React.ComponentType<CellRendererProps<ItemT>>
| null
| undefined;
}
@@ -748,29 +748,31 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
const end = getItemCount(data) - 1;
let prevCellKey;
last = Math.min(end, last);
for (let ii = first; ii <= last; ii++) {
const item = getItem(data, ii);
const key = this._keyExtractor(item, ii, this.props);
this._indicesToKeys.set(ii, key);
if (stickyIndicesFromProps.has(ii + stickyOffset)) {
stickyHeaderIndices.push(cells.length);
}
const shouldListenForLayout =
getItemLayout == null || debug || this._fillRateHelper.enabled();
cells.push(
<CellRenderer
CellRendererComponent={CellRendererComponent}
ItemSeparatorComponent={ii < end ? ItemSeparatorComponent : undefined}
ListItemComponent={ListItemComponent}
cellKey={key}
debug={debug}
fillRateHelper={this._fillRateHelper}
getItemLayout={getItemLayout}
horizontal={horizontal}
index={ii}
inversionStyle={inversionStyle}
item={item}
key={key}
prevCellKey={prevCellKey}
onCellLayout={this._onCellLayout}
onUpdateSeparators={this._onUpdateSeparators}
onCellFocusCapture={e => this._onCellFocusCapture(key)}
onUnmount={this._onCellUnmount}
@@ -778,6 +780,9 @@ class VirtualizedList extends StateSafePureComponent<Props, State> {
this._cellRefs[key] = ref;
}}
renderItem={renderItem}
{...(shouldListenForLayout && {
onCellLayout: this._onCellLayout,
})}
/>,
);
prevCellKey = key;
@@ -13,8 +13,7 @@ import type {
FocusEvent,
LayoutEvent,
} from 'react-native/Libraries/Types/CoreEventTypes';
import type FillRateHelper from './FillRateHelper';
import type {RenderItemType} from './VirtualizedListProps';
import type {CellRendererProps, RenderItemType} from './VirtualizedListProps';
import {View, StyleSheet} from 'react-native';
import {VirtualizedListCellContextProvider} from './VirtualizedListContext.js';
@@ -22,28 +21,17 @@ import invariant from 'invariant';
import * as React from 'react';
export type Props<ItemT> = {
CellRendererComponent?: ?React.ComponentType<any>,
CellRendererComponent?: ?React.ComponentType<CellRendererProps<ItemT>>,
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,
onCellLayout?: (event: LayoutEvent, cellKey: string, index: number) => void,
onCellFocusCapture?: (event: FocusEvent) => void,
onUnmount: (cellKey: string) => void,
onUpdateSeparators: (
@@ -181,14 +169,13 @@ export default class CellRenderer<ItemT> extends React.Component<
CellRendererComponent,
ItemSeparatorComponent,
ListItemComponent,
debug,
fillRateHelper,
getItemLayout,
cellKey,
horizontal,
item,
index,
inversionStyle,
onCellFocusCapture,
onCellLayout,
renderItem,
} = this.props;
const element = this._renderElement(
@@ -198,11 +185,6 @@ export default class CellRenderer<ItemT> extends React.Component<
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.Node = React.isValidElement(
@@ -224,21 +206,19 @@ export default class CellRenderer<ItemT> extends React.Component<
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. */
>
{...(onCellLayout && {onLayout: this._onLayout})}>
{element}
{itemSeparator}
</View>
) : (
<CellRendererComponent
{...this.props}
cellKey={cellKey}
index={index}
item={item}
style={cellStyle}
onLayout={onLayout}
onFocusCapture={onCellFocusCapture}>
onFocusCapture={onCellFocusCapture}
{...(onCellLayout && {onLayout: this._onLayout})}>
{element}
{itemSeparator}
</CellRendererComponent>
@@ -9,6 +9,10 @@
*/
import {typeof ScrollView} from 'react-native';
import type {
FocusEvent,
LayoutEvent,
} from 'react-native/Libraries/Types/CoreEventTypes';
import type {ViewStyleProp} from 'react-native/Libraries/StyleSheet/StyleSheet';
import type {
ViewabilityConfig,
@@ -34,6 +38,16 @@ export type RenderItemProps<ItemT> = {
...
};
export type CellRendererProps<ItemT> = $ReadOnly<{
cellKey: string,
children: React.Node,
index: number,
item: ItemT,
onFocusCapture?: (event: FocusEvent) => void,
onLayout?: (event: LayoutEvent) => void,
style: ViewStyleProp,
}>;
export type RenderItemType<ItemT> = (
info: RenderItemProps<ItemT>,
) => React.Node;
@@ -102,10 +116,12 @@ type OptionalProps = {|
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 allows customizing how cells rendered by
* `renderItem`/`ListItemComponent` are wrapped when placed into the
* underlying ScrollView. This component must accept event handlers which
* notify VirtualizedList of changes within the cell.
*/
CellRendererComponent?: ?React.ComponentType<any>,
CellRendererComponent?: ?React.ComponentType<CellRendererProps<Item>>,
/**
* 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`
+1
View File
@@ -24,6 +24,7 @@ export type {
ViewabilityConfigCallbackPair,
} from './Lists/ViewabilityHelper';
export type {
CellRendererProps,
RenderItemProps,
RenderItemType,
Separators,