From 2d41e6642eaee636f90d5737ecdcddf2d89cfa2a Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Tue, 14 Mar 2023 15:32:05 -0700 Subject: [PATCH] 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 --- .../Lists/VirtualizedList.d.ts | 21 +++++++++- .../Lists/VirtualizedList.js | 13 ++++-- .../Lists/VirtualizedListCellRenderer.js | 42 +++++-------------- .../Lists/VirtualizedListProps.js | 22 ++++++++-- packages/virtualized-lists/index.js | 1 + 5 files changed, 60 insertions(+), 39 deletions(-) diff --git a/packages/virtualized-lists/Lists/VirtualizedList.d.ts b/packages/virtualized-lists/Lists/VirtualizedList.d.ts index f7689d00439393..fc000b5f15caeb 100644 --- a/packages/virtualized-lists/Lists/VirtualizedList.d.ts +++ b/packages/virtualized-lists/Lists/VirtualizedList.d.ts @@ -87,6 +87,16 @@ export type ListRenderItem = ( info: ListRenderItemInfo, ) => React.ReactElement | null; +export interface CellRendererProps { + cellKey: string; + children: React.ReactNode; + index: number; + item: ItemT; + onFocusCapture?: ((event: FocusEvent) => void) | undefined; + onLayout?: ((event: LayoutChangeEvent) => void) | undefined; + style: StyleProp | undefined; +} + /** * @see https://reactnative.dev/docs/virtualizedlist */ @@ -370,5 +380,14 @@ export interface VirtualizedListWithoutRenderItemProps */ windowSize?: number | undefined; - CellRendererComponent?: React.ComponentType | 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> + | null + | undefined; } diff --git a/packages/virtualized-lists/Lists/VirtualizedList.js b/packages/virtualized-lists/Lists/VirtualizedList.js index 7843ad87cfda01..193484036f5911 100644 --- a/packages/virtualized-lists/Lists/VirtualizedList.js +++ b/packages/virtualized-lists/Lists/VirtualizedList.js @@ -748,29 +748,31 @@ class VirtualizedList extends StateSafePureComponent { 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( this._onCellFocusCapture(key)} onUnmount={this._onCellUnmount} @@ -778,6 +780,9 @@ class VirtualizedList extends StateSafePureComponent { this._cellRefs[key] = ref; }} renderItem={renderItem} + {...(shouldListenForLayout && { + onCellLayout: this._onCellLayout, + })} />, ); prevCellKey = key; diff --git a/packages/virtualized-lists/Lists/VirtualizedListCellRenderer.js b/packages/virtualized-lists/Lists/VirtualizedListCellRenderer.js index 0064f788b8a9c4..91e1a62ad911c9 100644 --- a/packages/virtualized-lists/Lists/VirtualizedListCellRenderer.js +++ b/packages/virtualized-lists/Lists/VirtualizedListCellRenderer.js @@ -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 = { - CellRendererComponent?: ?React.ComponentType, + CellRendererComponent?: ?React.ComponentType>, ItemSeparatorComponent: ?React.ComponentType< any | {highlighted: boolean, leadingItem: ?ItemT}, >, ListItemComponent?: ?(React.ComponentType | React.Element), 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 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 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 extends React.Component< const result = !CellRendererComponent ? ( =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} ) : ( + onFocusCapture={onCellFocusCapture} + {...(onCellLayout && {onLayout: this._onLayout})}> {element} {itemSeparator} diff --git a/packages/virtualized-lists/Lists/VirtualizedListProps.js b/packages/virtualized-lists/Lists/VirtualizedListProps.js index bfc59673270a57..3e196a9a9bac6a 100644 --- a/packages/virtualized-lists/Lists/VirtualizedListProps.js +++ b/packages/virtualized-lists/Lists/VirtualizedListProps.js @@ -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 = { ... }; +export type CellRendererProps = $ReadOnly<{ + cellKey: string, + children: React.Node, + index: number, + item: ItemT, + onFocusCapture?: (event: FocusEvent) => void, + onLayout?: (event: LayoutEvent) => void, + style: ViewStyleProp, +}>; + export type RenderItemType = ( info: RenderItemProps, ) => 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, + CellRendererComponent?: ?React.ComponentType>, /** * 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` diff --git a/packages/virtualized-lists/index.js b/packages/virtualized-lists/index.js index 8516f7ccf73289..31c196ba5e4e77 100644 --- a/packages/virtualized-lists/index.js +++ b/packages/virtualized-lists/index.js @@ -24,6 +24,7 @@ export type { ViewabilityConfigCallbackPair, } from './Lists/ViewabilityHelper'; export type { + CellRendererProps, RenderItemProps, RenderItemType, Separators,