diff --git a/Libraries/Components/ScrollView/ScrollViewStickyHeader.js b/Libraries/Components/ScrollView/ScrollViewStickyHeader.js index 0510fae6b58fc9..86c10a1fcfa257 100644 --- a/Libraries/Components/ScrollView/ScrollViewStickyHeader.js +++ b/Libraries/Components/ScrollView/ScrollViewStickyHeader.js @@ -59,7 +59,7 @@ class ScrollViewStickyHeader extends React.Component { this.props.onLayout(event); const child = React.Children.only(this.props.children); if (child.props.onLayout) { - child.props.onLayout(event); + child.props.onLayout(event, child.props.cellKey, child.props.index); } }; diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index d060dbf9fa6d28..b4cbbabf7a6819 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -690,6 +690,11 @@ class VirtualizedList extends React.PureComponent { getItemCount, horizontal, keyExtractor, + getItemLayout, + renderItem, + ListItemComponent, + extraData, + debug, } = this.props; const stickyOffset = this.props.ListHeaderComponent ? 1 : 0; const end = getItemCount(data) - 1; @@ -715,9 +720,13 @@ class VirtualizedList extends React.PureComponent { key={key} prevCellKey={prevCellKey} onUpdateSeparators={this._onUpdateSeparators} - onLayout={e => this._onCellLayout(e, key, ii)} + onLayout={this._onCellLayout} onUnmount={this._onCellUnmount} - parentProps={this.props} + getItemLayout={getItemLayout} + renderItem={renderItem} + ListItemComponent={ListItemComponent} + extraData={extraData} + debug={debug} ref={ref => { this._cellRefs[key] = ref; }} @@ -1100,7 +1109,7 @@ class VirtualizedList extends React.PureComponent { } }; - _onCellLayout(e, cellKey, index) { + _onCellLayout = (e, cellKey, index): void => { const layout = e.nativeEvent.layout; const next = { offset: this._selectOffset(layout), @@ -1141,7 +1150,7 @@ class VirtualizedList extends React.PureComponent { this._computeBlankness(); this._updateViewableItems(this.props.data); - } + }; _onCellUnmount = (cellKey: string) => { const curr = this._frames[cellKey]; @@ -1676,15 +1685,14 @@ type CellRendererProps = { index: number, inversionStyle: ViewStyleProp, item: Item, - onLayout: (event: Object) => void, // This is extracted by ScrollViewStickyHeader + onLayout: (event: Object, key: string, index: number) => void, // This is extracted by ScrollViewStickyHeader onUnmount: (cellKey: string) => void, onUpdateSeparators: (cellKeys: Array, props: Object) => void, - parentProps: { - getItemLayout?: ?Function, - renderItem?: ?RenderItemType, - ListItemComponent?: ?(React.ComponentType | React.Element), - }, prevCellKey: ?string, + getItemLayout?: ?Function, + renderItem: $PropertyType, + ListItemComponent?: ?(React.ComponentType | React.Element), + debug: ?boolean, }; type CellRendererState = { @@ -1765,6 +1773,10 @@ class CellRenderer extends React.Component< this.props.onUnmount(this.props.cellKey); } + _onLayout = (e): void => + this.props.onLayout && + this.props.onLayout(e, this.props.cellKey, this.props.index); + _renderElement(renderItem, ListItemComponent, item, index) { if (renderItem && ListItemComponent) { console.warn( @@ -1799,14 +1811,17 @@ class CellRenderer extends React.Component< const { CellRendererComponent, ItemSeparatorComponent, + ListItemComponent, fillRateHelper, horizontal, item, index, inversionStyle, - parentProps, + renderItem, + getItemLayout, + debug, } = this.props; - const {renderItem, getItemLayout, ListItemComponent} = parentProps; + const element = this._renderElement( renderItem, ListItemComponent, @@ -1815,12 +1830,9 @@ class CellRenderer extends React.Component< ); const onLayout = - /* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an - * error found when Flow v0.68 was deployed. To see the error delete this - * comment and run Flow. */ - getItemLayout && !parentProps.debug && !fillRateHelper.enabled() + getItemLayout && !debug && !fillRateHelper.enabled() ? undefined - : this.props.onLayout; + : this._onLayout; // NOTE: that when this is a sticky header, `onLayout` will get automatically extracted and // called explicitly by `ScrollViewStickyHeader`. const itemSeparator = ItemSeparatorComponent && ( diff --git a/Libraries/Lists/__tests__/VirtualizedList-test.js b/Libraries/Lists/__tests__/VirtualizedList-test.js index c4873374e0ef54..89961cc486a74f 100644 --- a/Libraries/Lists/__tests__/VirtualizedList-test.js +++ b/Libraries/Lists/__tests__/VirtualizedList-test.js @@ -273,4 +273,59 @@ describe('VirtualizedList', () => { }), ); }); + + it('calls _onCellLayout properly', () => { + const items = [{key: 'i1'}, {key: 'i2'}, {key: 'i3'}]; + const mock = jest.fn(); + const component = ReactTestRenderer.create( + } + getItem={(data, index) => data[index]} + getItemCount={data => data.length} + />, + ); + const virtualList: VirtualizedList = component.getInstance(); + virtualList._onCellLayout = mock; + component.update( + } + getItem={(data, index) => data[index]} + getItemCount={data => data.length} + />, + ); + const cell = virtualList._cellRefs.i4; + const event = { + nativeEvent: {layout: {x: 0, y: 0, width: 50, height: 50}}, + }; + cell._onLayout(event); + expect(mock).toHaveBeenCalledWith(event, 'i4', 3); + }); + + it('handles extraData correctly', () => { + const mock = jest.fn(); + const listData = [{key: 'i0'}, {key: 'i1'}, {key: 'i2'}]; + const getItem = (data, index) => data[index]; + const getItemCount = data => data.length; + const component = ReactTestRenderer.create( + , + ); + + component.update( + , + ); + expect(mock).toHaveBeenCalledTimes(6); + }); });