Skip to content

Commit

Permalink
Reduce unnecessary renders for CellMeasurer in Grid (#969)
Browse files Browse the repository at this point in the history
  • Loading branch information
bvaughn authored Jan 15, 2018
1 parent 04de423 commit 3faeb4e
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 46 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Changelog

##### 9.17.1
* 🐛 `CellMeasurer` works properly in iframes and popup windows. ([dfdeagle47](https://github.com/dfdeagle47) - [#968](https://github.com/bvaughn/react-virtualized/pull/968))
* ✨ Eliminate unnecessary renders for `CellMeasurer` and `Grid`. ([bvaughn](https://github.com/bvaughn) - [#969](https://github.com/bvaughn/react-virtualized/pull/969))

##### 9.17.0

Expand Down
64 changes: 47 additions & 17 deletions source/Grid/Grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,20 +312,12 @@ export default class Grid extends React.PureComponent<Props, State> {
this._columnWidthGetter = this._wrapSizeGetter(props.columnWidth);
this._rowHeightGetter = this._wrapSizeGetter(props.rowHeight);

const deferredMeasurementCache = props.deferredMeasurementCache;

this._columnSizeAndPositionManager = new ScalingCellSizeAndPositionManager({
batchAllCells:
deferredMeasurementCache !== undefined &&
!deferredMeasurementCache.hasFixedHeight(),
cellCount: props.columnCount,
cellSizeGetter: params => this._columnWidthGetter(params),
estimatedCellSize: this._getEstimatedColumnSize(props),
});
this._rowSizeAndPositionManager = new ScalingCellSizeAndPositionManager({
batchAllCells:
deferredMeasurementCache !== undefined &&
!deferredMeasurementCache.hasFixedWidth(),
cellCount: props.rowCount,
cellSizeGetter: params => this._rowHeightGetter(params),
estimatedCellSize: this._getEstimatedRowSize(props),
Expand Down Expand Up @@ -1018,12 +1010,12 @@ export default class Grid extends React.PureComponent<Props, State> {
overscanCellsCount: overscanColumnCount,
scrollDirection: scrollDirectionHorizontal,
startIndex:
typeof this._renderedColumnStartIndex === 'number'
? this._renderedColumnStartIndex
typeof visibleColumnIndices.start === 'number'
? visibleColumnIndices.start
: 0,
stopIndex:
typeof this._renderedColumnStopIndex === 'number'
? this._renderedColumnStopIndex
typeof visibleColumnIndices.stop === 'number'
? visibleColumnIndices.stop
: -1,
});

Expand All @@ -1033,14 +1025,13 @@ export default class Grid extends React.PureComponent<Props, State> {
overscanCellsCount: overscanRowCount,
scrollDirection: scrollDirectionVertical,
startIndex:
typeof this._renderedRowStartIndex === 'number'
? this._renderedRowStartIndex
typeof visibleRowIndices.start === 'number'
? visibleRowIndices.start
: 0,
stopIndex:
typeof this._renderedRowStopIndex === 'number'
? this._renderedRowStopIndex
typeof visibleRowIndices.stop === 'number'
? visibleRowIndices.stop
: -1,
// stopIndex: this._renderedRowStopIndex
});

// Store for _invokeOnGridRenderedHelper()
Expand All @@ -1049,6 +1040,45 @@ export default class Grid extends React.PureComponent<Props, State> {
this._rowStartIndex = overscanRowIndices.overscanStartIndex;
this._rowStopIndex = overscanRowIndices.overscanStopIndex;

// Advanced use-cases (eg CellMeasurer) require batched measurements to determine accurate sizes.
if (deferredMeasurementCache) {
// If rows have a dynamic height, scan the rows we are about to render.
// If any have not yet been measured, then we need to render all columns initially,
// Because the height of the row is equal to the tallest cell within that row,
// (And so we can't know the height without measuring all column-cells first).
if (!deferredMeasurementCache.hasFixedHeight()) {
for (
let rowIndex = this._rowStartIndex;
rowIndex <= this._rowStopIndex;
rowIndex++
) {
if (!deferredMeasurementCache.has(rowIndex, 0)) {
this._columnStartIndex = 0;
this._columnStopIndex = columnCount - 1;
break;
}
}
}

// If columns have a dynamic width, scan the columns we are about to render.
// If any have not yet been measured, then we need to render all rows initially,
// Because the width of the column is equal to the widest cell within that column,
// (And so we can't know the width without measuring all row-cells first).
if (!deferredMeasurementCache.hasFixedWidth()) {
for (
let columnIndex = this._columnStartIndex;
columnIndex <= this._columnStopIndex;
columnIndex++
) {
if (!deferredMeasurementCache.has(0, columnIndex)) {
this._rowStartIndex = 0;
this._rowStopIndex = rowCount - 1;
break;
}
}
}
}

this._childrenToDisplay = cellRangeRenderer({
cellCache: this._cellCache,
cellRenderer,
Expand Down
15 changes: 0 additions & 15 deletions source/Grid/utils/CellSizeAndPositionManager.jest.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ import CellSizeAndPositionManager from './CellSizeAndPositionManager';

describe('CellSizeAndPositionManager', () => {
function getCellSizeAndPositionManager({
batchAllCells,
cellCount = 100,
estimatedCellSize = 15,
} = {}) {
const cellSizeGetterCalls = [];
const cellSizeAndPositionManager = new CellSizeAndPositionManager({
batchAllCells,
cellCount,
cellSizeGetter: ({index}) => {
cellSizeGetterCalls.push(index);
Expand Down Expand Up @@ -382,19 +380,6 @@ describe('CellSizeAndPositionManager', () => {
expect(start).toEqual(95);
expect(stop).toEqual(99);
});

it('should return all cells if :batchAllCells param was used (for CellMeasurer support)', () => {
const {cellSizeAndPositionManager} = getCellSizeAndPositionManager({
batchAllCells: true,
cellCount: 100,
});
const {start, stop} = cellSizeAndPositionManager.getVisibleCellRange({
containerSize: 50,
offset: 950,
});
expect(start).toEqual(0);
expect(stop).toEqual(99);
});
});

describe('resetCell', () => {
Expand Down
13 changes: 0 additions & 13 deletions source/Grid/utils/CellSizeAndPositionManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import type {Alignment, CellSizeGetter, VisibleCellRange} from '../types';

type CellSizeAndPositionManagerParams = {
batchAllCells: boolean,
cellCount: number,
cellSizeGetter: CellSizeGetter,
estimatedCellSize: number,
Expand Down Expand Up @@ -46,18 +45,15 @@ export default class CellSizeAndPositionManager {
// Used in deferred mode to track which cells have been queued for measurement.
_lastBatchedIndex = -1;

_batchAllCells: boolean;
_cellCount: number;
_cellSizeGetter: CellSizeGetter;
_estimatedCellSize: number;

constructor({
batchAllCells = false,
cellCount,
cellSizeGetter,
estimatedCellSize,
}: CellSizeAndPositionManagerParams) {
this._batchAllCells = batchAllCells;
this._cellSizeGetter = cellSizeGetter;
this._cellCount = cellCount;
this._estimatedCellSize = estimatedCellSize;
Expand Down Expand Up @@ -208,15 +204,6 @@ export default class CellSizeAndPositionManager {
}

getVisibleCellRange(params: GetVisibleCellRangeParams): VisibleCellRange {
// Advanced use-cases (eg CellMeasurer) require batched measurements to determine accurate sizes.
// eg we can't know a row's height without measuring the height of all columns within that row.
if (this._batchAllCells) {
return {
start: 0,
stop: this._cellCount - 1,
};
}

let {containerSize, offset} = params;

const totalSize = this.getTotalSize();
Expand Down
1 change: 0 additions & 1 deletion source/Grid/utils/ScalingCellSizeAndPositionManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ type ContainerSizeAndOffset = {

type Params = {
maxScrollSize?: number,
batchAllCells: boolean,
cellCount: number,
cellSizeGetter: CellSizeGetter,
estimatedCellSize: number,
Expand Down

0 comments on commit 3faeb4e

Please sign in to comment.