diff --git a/CHANGELOG.md b/CHANGELOG.md index 57d56dbc1..c0252a5b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Changelog * ✨ Replaced inline `require` statement with header `import` in `Grid` for better integration with the Rollup module bundler. ([@odogono](https://github.com/odogono) - [#617](https://github.com/bvaughn/react-virtualized/pull/617)) * 🐛 Improved guard for edge-case scrolling issue with rubberband scrolling in iOS. ([@dtoddtarsi](https://github.com/offsky) - [#616](https://github.com/bvaughn/react-virtualized/pull/616)) * ✨ Replaced `getBoundingClientRect()` with slightly faster `offsetWidth` and `offsetHeight` inside of `AutoSizer`. +* ✨ `AutoSizer` no longer re-renders nor calls `onResize` callback unless `width` and/or `height` have changed (depending on which properties are being watched). ##### 9.3.0 * 🎉 Added `resetLoadMoreRowsCache` method to `InfiniteLoader` to reset any cached data about loaded rows. This method should be called if any/all loaded data needs to be refetched (eg a filtered list where the search criteria changes). ([#612](https://github.com/bvaughn/react-virtualized/issues/612)) diff --git a/source/AutoSizer/AutoSizer.jest.js b/source/AutoSizer/AutoSizer.jest.js index f4c6933f1..f9d33fc59 100644 --- a/source/AutoSizer/AutoSizer.jest.js +++ b/source/AutoSizer/AutoSizer.jest.js @@ -5,7 +5,7 @@ import { findDOMNode } from 'react-dom' import { render } from '../TestUtils' import AutoSizer from './AutoSizer' -function ChildComponent ({ height, width, foo, bar }) { +function DefaultChildComponent ({ height, width, foo, bar }) { return (
{`width:${width}, height:${height}, foo:${foo}, bar:${bar}`}
) @@ -14,10 +14,12 @@ function ChildComponent ({ height, width, foo, bar }) { describe('AutoSizer', () => { function getMarkup ({ bar = 123, + ChildComponent = DefaultChildComponent, disableHeight = false, disableWidth = false, foo = 456, height = 100, + onResize, paddingBottom = 0, paddingLeft = 0, paddingRight = 0, @@ -38,7 +40,11 @@ describe('AutoSizer', () => { return (
- + {({ height, width }) => ( { expect(rendered.textContent).toContain('width:300') done() }) + + describe('onResize and (re)render', () => { + it('should trigger when size changes', async (done) => { + const onResize = jest.fn() + const ChildComponent = jest.fn().mockImplementation(DefaultChildComponent) + const rendered = findDOMNode(render(getMarkup({ + ChildComponent, + height: 100, + onResize, + width: 200 + }))) + ChildComponent.mockClear() // TODO Improve initial check in version 10; see AutoSizer render() + expect(onResize).toHaveBeenCalledTimes(1) + await simulateResize({ element: rendered, height: 400, width: 300 }) + expect(ChildComponent).toHaveBeenCalledTimes(1) + expect(onResize).toHaveBeenCalledTimes(2) + done() + }) + + it('should only trigger when height changes for disableWidth == true', async (done) => { + const onResize = jest.fn() + const ChildComponent = jest.fn().mockImplementation(DefaultChildComponent) + const rendered = findDOMNode(render(getMarkup({ + ChildComponent, + disableWidth: true, + height: 100, + onResize, + width: 200 + }))) + ChildComponent.mockClear() // TODO Improve initial check in version 10; see AutoSizer render() + expect(onResize).toHaveBeenCalledTimes(1) + await simulateResize({ element: rendered, height: 100, width: 300 }) + expect(ChildComponent).toHaveBeenCalledTimes(0) + expect(onResize).toHaveBeenCalledTimes(1) + await simulateResize({ element: rendered, height: 200, width: 300 }) + expect(ChildComponent).toHaveBeenCalledTimes(1) + expect(onResize).toHaveBeenCalledTimes(2) + done() + }) + + it('should only trigger when width changes for disableHeight == true', async (done) => { + const onResize = jest.fn() + const ChildComponent = jest.fn().mockImplementation(DefaultChildComponent) + const rendered = findDOMNode(render(getMarkup({ + ChildComponent, + disableHeight: true, + height: 100, + onResize, + width: 200 + }))) + ChildComponent.mockClear() // TODO Improve initial check in version 10; see AutoSizer render() + expect(onResize).toHaveBeenCalledTimes(1) + await simulateResize({ element: rendered, height: 200, width: 200 }) + expect(ChildComponent).toHaveBeenCalledTimes(0) + expect(onResize).toHaveBeenCalledTimes(1) + await simulateResize({ element: rendered, height: 200, width: 300 }) + expect(ChildComponent).toHaveBeenCalledTimes(1) + expect(onResize).toHaveBeenCalledTimes(2) + done() + }) + }) }) diff --git a/source/AutoSizer/AutoSizer.js b/source/AutoSizer/AutoSizer.js index 426aaa8b6..ffef537cb 100644 --- a/source/AutoSizer/AutoSizer.js +++ b/source/AutoSizer/AutoSizer.js @@ -79,6 +79,18 @@ export default class AutoSizer extends PureComponent { outerStyle.width = 0 } + /** + * TODO: Avoid rendering children before the initial measurements have been collected. + * At best this would just be wasting cycles. + * Add this check into version 10 though as it could break too many ref callbacks in version 9. + if ( + height !== 0 && + width !== 0 + ) { + child = children({ height, width }) + } + */ + return (