diff --git a/packages/@react-spectrum/list/stories/ListView.stories.tsx b/packages/@react-spectrum/list/stories/ListView.stories.tsx index 4ce91b6d404..3c4cb6d822f 100644 --- a/packages/@react-spectrum/list/stories/ListView.stories.tsx +++ b/packages/@react-spectrum/list/stories/ListView.stories.tsx @@ -517,3 +517,32 @@ function Demo(props) { ); } + +const manyItems = []; +for (let i = 0; i < 500; i++) {manyItems.push({key: i, name: `item ${i}`});} + +function DisplayNoneComponent(args) { + const [isDisplay, setIsDisplay] = useState(false); + + return ( + <> + +
+ + {(item: any) => ( + + + {item.name} + + + )} + +
+ + ); +} + +export const DisplayNone: ListViewStory = { + render: (args) => , + name: 'display: none with many items' +}; diff --git a/packages/@react-spectrum/list/test/ListView.test.js b/packages/@react-spectrum/list/test/ListView.test.js index 6f4eab54f16..5ab989dfa75 100644 --- a/packages/@react-spectrum/list/test/ListView.test.js +++ b/packages/@react-spectrum/list/test/ListView.test.js @@ -1664,4 +1664,28 @@ describe('ListView', function () { }); }); }); + + describe('height 0', () => { + + it('should render and not infinite loop', function () { + offsetWidth = jest.spyOn(window.HTMLElement.prototype, 'clientWidth', 'get').mockImplementation(() => 0); + offsetHeight = jest.spyOn(window.HTMLElement.prototype, 'clientHeight', 'get').mockImplementation(() => 0); + scrollHeight = jest.spyOn(window.HTMLElement.prototype, 'scrollHeight', 'get').mockImplementation(() => 0); + let tree = render( + ({key: k, name: `Item ${k}`}))}> + {item => {item.name}} + + ); + let grid = tree.getByRole('grid'); + act(() => { + jest.runAllTimers(); + }); + expect(grid.firstChild).toBeEmptyDOMElement(); + }); + }); }); diff --git a/packages/@react-stately/virtualizer/src/OverscanManager.ts b/packages/@react-stately/virtualizer/src/OverscanManager.ts index 8e5bf126a2a..a12cc686b87 100644 --- a/packages/@react-stately/virtualizer/src/OverscanManager.ts +++ b/packages/@react-stately/virtualizer/src/OverscanManager.ts @@ -17,7 +17,7 @@ export class OverscanManager { private startTime = 0; private velocity = new Point(0, 0); private visibleRect = new Rect(); - + setVisibleRect(rect: Rect) { let time = performance.now() - this.startTime; if (time < 500) { diff --git a/packages/@react-stately/virtualizer/src/Virtualizer.ts b/packages/@react-stately/virtualizer/src/Virtualizer.ts index 7940dcabbdb..36ed878f948 100644 --- a/packages/@react-stately/virtualizer/src/Virtualizer.ts +++ b/packages/@react-stately/virtualizer/src/Virtualizer.ts @@ -23,8 +23,8 @@ import {Size} from './Size'; /** * The Virtualizer class renders a scrollable collection of data using customizable layouts. - * It supports very large collections by only rendering visible views to the DOM, reusing - * them as you scroll. Virtualizer can present any type of view, including non-item views + * It supports very large collections by only rendering visible views to the DOM, reusing + * them as you scroll. Virtualizer can present any type of view, including non-item views * such as section headers and footers. * * Virtualizer uses {@link Layout} objects to compute what views should be visible, and how @@ -133,7 +133,7 @@ export class Virtualizer { */ keyAtPoint(point: Point): Key | null { let rect = new Rect(point.x, point.y, 1, 1); - let layoutInfos = this.layout.getVisibleLayoutInfos(rect); + let layoutInfos = rect.area === 0 ? [] : this.layout.getVisibleLayoutInfos(rect); // Layout may return multiple layout infos in the case of // persisted keys, so find the first one that actually intersects. @@ -180,7 +180,7 @@ export class Virtualizer { rect = this._overscanManager.getOverscannedRect(); } - let layoutInfos = this.layout.getVisibleLayoutInfos(rect); + let layoutInfos = rect.area === 0 ? [] : this.layout.getVisibleLayoutInfos(rect); let map = new Map; for (let layoutInfo of layoutInfos) { map.set(layoutInfo.key, layoutInfo); @@ -273,7 +273,7 @@ export class Virtualizer { if (!this.visibleRect.equals(opts.visibleRect)) { this._overscanManager.setVisibleRect(opts.visibleRect); let shouldInvalidate = this.layout.shouldInvalidate(opts.visibleRect, this.visibleRect); - + if (shouldInvalidate) { offsetChanged = !opts.visibleRect.pointEquals(this.visibleRect); sizeChanged = !opts.visibleRect.sizeEquals(this.visibleRect);