diff --git a/packages/virtualized-lists/Lists/VirtualizedList.js b/packages/virtualized-lists/Lists/VirtualizedList.js index 031267465af8..18b536b12e38 100644 --- a/packages/virtualized-lists/Lists/VirtualizedList.js +++ b/packages/virtualized-lists/Lists/VirtualizedList.js @@ -1017,9 +1017,7 @@ class VirtualizedList extends StateSafePureComponent< onUpdateSeparators={this._onUpdateSeparators} onCellFocusCapture={this._onCellFocusCapture} onUnmount={this._onCellUnmount} - ref={ref => { - this._cellRefs[key] = ref; - }} + ref={this._captureCellRef} renderItem={renderItem} {...(shouldListenForLayout && { onCellLayout: this._onCellLayout, @@ -1610,6 +1608,14 @@ class VirtualizedList extends StateSafePureComponent< } }; + // Stable, so React doesn't detach and reattach every cell's ref on each + // render. Unmounted cells are removed in `_onCellUnmount`. + _captureCellRef = (cellRenderer: null | CellRenderer): void => { + if (cellRenderer != null) { + this._cellRefs[cellRenderer.props.cellKey] = cellRenderer; + } + }; + _onCellUnmount = (cellKey: string) => { delete this._cellRefs[cellKey]; this._listMetrics.notifyCellUnmounted(cellKey); diff --git a/packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js b/packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js index 5bc0254c7127..bb0ea124c632 100644 --- a/packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js +++ b/packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js @@ -294,6 +294,59 @@ describe('VirtualizedList', () => { }); }); + it('does not reattach cell refs when the list re-renders', async () => { + const listRef = createRef(); + const items = generateItems(5); + let component; + await act(() => { + component = create( + , + ); + }); + + const cellRefs = listRef.current._cellRefs; + const cellRefWrites = []; + listRef.current._cellRefs = new Proxy(cellRefs, { + set(target, cellKey, cellRenderer) { + cellRefWrites.push(cellKey); + target[cellKey] = cellRenderer; + return true; + }, + }); + + await act(() => { + component.update( + , + ); + }); + + expect(cellRefWrites).toEqual([]); + expect(Object.keys(cellRefs)).toEqual(['0', '1', '2', '3', '4']); + }); + + it('removes cell refs when cells unmount', async () => { + const listRef = createRef(); + let component; + await act(() => { + component = create( + , + ); + }); + + await act(() => { + component.update( + , + ); + }); + + expect(Object.keys(listRef.current._cellRefs)).toEqual(['0', '1']); + expect(listRef.current._cellRefs[0].props.cellKey).toBe(0); + }); + it('handles nested lists', async () => { let component; await act(() => {