From a010a0cebd4afc0d88336c2c265a5d9dbb19918f Mon Sep 17 00:00:00 2001 From: Logan Daniels Date: Tue, 19 Dec 2017 13:43:08 -0800 Subject: [PATCH] Fix virtualized cell keys for list headers and footers Summary: The change enabling virtualization in nested lists contained a hidden assumption that nested lists would only appear within the *cells* of a parent list. If a list header or footer component contains a `VirtualizedList`, that child list won't be wrapped in a `CellRenderer` component and therefore won't have access to `virtualizedCellRenderer` through its context. This causes an error when the child list tries to access the `cellKey` property on an undefined object. This change wraps the header/footer views in a `VirtualizedCellWrapper` component which supplies that context properly. Reviewed By: sahrens Differential Revision: D6603342 fbshipit-source-id: 4d2d82f04947048a16ec9968121d8ecc8c95655a --- Libraries/Lists/VirtualizedList.js | 74 ++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index 95fb0cda6..77c01bd6c 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -432,7 +432,7 @@ class VirtualizedList extends React.PureComponent { }; static contextTypes = { - virtualizedListCellRenderer: PropTypes.shape({ + virtualizedCell: PropTypes.shape({ cellKey: PropTypes.string, }), virtualizedList: PropTypes.shape({ @@ -466,6 +466,13 @@ class VirtualizedList extends React.PureComponent { }; } + _getCellKey(): string { + return ( + (this.context.virtualizedCell && this.context.virtualizedCell.cellKey) || + 'rootList' + ); + } + _getScrollMetrics = () => { return this._scrollMetrics; }; @@ -559,10 +566,8 @@ class VirtualizedList extends React.PureComponent { if (this._isNestedWithSameOrientation()) { const storedState = this.context.virtualizedList.registerAsNestedChild({ - cellKey: this.context.virtualizedListCellRenderer.cellKey, - key: - this.props.listKey || - this.context.virtualizedListCellRenderer.cellKey, + cellKey: this._getCellKey(), + key: this.props.listKey || this._getCellKey(), ref: this, }); if (storedState) { @@ -575,8 +580,6 @@ class VirtualizedList extends React.PureComponent { this.state = initialState; } - componentWillMount() {} - componentDidMount() { if (this.props.initialScrollIndex) { this._initialScrollIndexTimeout = setTimeout( @@ -593,9 +596,7 @@ class VirtualizedList extends React.PureComponent { componentWillUnmount() { if (this._isNestedWithSameOrientation()) { this.context.virtualizedList.unregisterAsNestedChild({ - key: - this.props.listKey || - this.context.virtualizedListCellRenderer.cellKey, + key: this.props.listKey || this._getCellKey(), state: { first: this.state.first, last: this.state.last, @@ -743,12 +744,13 @@ class VirtualizedList extends React.PureComponent { ); cells.push( - - {element} - , + + + {element} + + , ); } const itemCount = this.props.getItemCount(data); @@ -867,12 +869,13 @@ class VirtualizedList extends React.PureComponent { ); cells.push( - - {element} - , + + + {element} + + , ); } const scrollProps = { @@ -1522,14 +1525,14 @@ class CellRenderer extends React.Component< }; static childContextTypes = { - virtualizedListCellRenderer: PropTypes.shape({ + virtualizedCell: PropTypes.shape({ cellKey: PropTypes.string, }), }; getChildContext() { return { - virtualizedListCellRenderer: { + virtualizedCell: { cellKey: this.props.cellKey, }, }; @@ -1621,6 +1624,29 @@ class CellRenderer extends React.Component< } } +class VirtualizedCellWrapper extends React.Component<{ + cellKey: string, + children: React.Node, +}> { + static childContextTypes = { + virtualizedCell: PropTypes.shape({ + cellKey: PropTypes.string, + }), + }; + + getChildContext() { + return { + virtualizedCell: { + cellKey: this.props.cellKey, + }, + }; + } + + render() { + return this.props.children; + } +} + const styles = StyleSheet.create({ verticallyInverted: { transform: [{scaleY: -1}],