Skip to content

Commit 438b567

Browse files
authored
Grid: Allow users to opt out of rerendering on scroll stop (bvaughn#1131)
* don't clear visible cells from cellCache if isScrollingOptOut is specified * adding test * adding docs * typo
1 parent c1d7377 commit 438b567

File tree

5 files changed

+70
-1
lines changed

5 files changed

+70
-1
lines changed

docs/Grid.md

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ A windowed grid of elements. `Grid` only renders cells necessary to fill itself
2323
| height | Number || Height of Grid; this property determines the number of visible (vs virtualized) rows. |
2424
| id | String | | Optional custom id to attach to root `Grid` element. |
2525
| isScrolling | Boolean | | Override internal is-scrolling state tracking. This property is primarily intended for use with the WindowScroller component. |
26+
| isScrollingOptOut | Boolean | | Prevents re-rendering of visible cells on scroll end. |
2627
| noContentRenderer | Function | | Optional renderer to be rendered inside the grid when either `rowCount` or `columnCount` is empty: `(): PropTypes.node` |
2728
| onSectionRendered | Function | | Callback invoked with information about the section of the Grid that was just rendered. This callback is only invoked when visible rows have changed: `({ columnOverscanStartIndex: number, columnOverscanStopIndex: number, columnStartIndex: number, columnStopIndex: number, rowOverscanStartIndex: number, rowOverscanStopIndex: number, rowStartIndex: number, rowStopIndex: number }): void` |
2829
| onScroll | Function | | Callback invoked whenever the scroll offset changes within the inner scrollable region: `({ clientHeight: number, clientWidth: number, scrollHeight: number, scrollLeft: number, scrollTop: number, scrollWidth: number }): void` |

source/Grid/Grid.jest.js

+46
Original file line numberDiff line numberDiff line change
@@ -1905,6 +1905,52 @@ describe('Grid', () => {
19051905
expect(cellRendererCalls.length).not.toEqual(0);
19061906
});
19071907

1908+
it('should not clear cache if :isScrollingOptOut is true', () => {
1909+
const cellRendererCalls = [];
1910+
function cellRenderer({columnIndex, key, rowIndex, style}) {
1911+
cellRendererCalls.push({columnIndex, rowIndex});
1912+
return defaultCellRenderer({columnIndex, key, rowIndex, style});
1913+
}
1914+
const props = {
1915+
cellRenderer,
1916+
columnWidth: 100,
1917+
height: 40,
1918+
rowHeight: 20,
1919+
scrollTop: 0,
1920+
width: 100,
1921+
isScrollingOptOut: true,
1922+
};
1923+
1924+
render(getMarkup(props));
1925+
render(getMarkup(props));
1926+
expect(cellRendererCalls).toEqual([
1927+
{columnIndex: 0, rowIndex: 0},
1928+
{columnIndex: 0, rowIndex: 1},
1929+
]);
1930+
1931+
cellRendererCalls.splice(0);
1932+
1933+
render(
1934+
getMarkup({
1935+
...props,
1936+
isScrolling: false,
1937+
}),
1938+
);
1939+
1940+
// Visible cells are cached
1941+
expect(cellRendererCalls.length).toEqual(0);
1942+
1943+
render(
1944+
getMarkup({
1945+
...props,
1946+
isScrolling: true,
1947+
}),
1948+
);
1949+
1950+
// Only cleared non-visible cells
1951+
expect(cellRendererCalls.length).toEqual(0);
1952+
});
1953+
19081954
it('should support a custom :scrollingResetTimeInterval prop', async done => {
19091955
const cellRendererCalls = [];
19101956
const scrollingResetTimeInterval =

source/Grid/Grid.js

+17
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ type Props = {
136136
*/
137137
isScrolling?: boolean,
138138

139+
/**
140+
* Opt-out of isScrolling param passed to cellRangeRenderer.
141+
* To avoid the extra render when scroll stops.
142+
*/
143+
isScrollingOptOut: boolean,
144+
139145
/** Optional renderer to be used in place of rows when either :rowCount or :columnCount is 0. */
140146
noContentRenderer: NoContentRenderer,
141147

@@ -276,6 +282,7 @@ class Grid extends React.PureComponent<Props, State> {
276282
scrollToRow: -1,
277283
style: {},
278284
tabIndex: 0,
285+
isScrollingOptOut: false,
279286
};
280287

281288
// Invokes onSectionRendered callback only when start/stop row or column indices change
@@ -1078,6 +1085,7 @@ class Grid extends React.PureComponent<Props, State> {
10781085
overscanRowCount,
10791086
rowCount,
10801087
width,
1088+
isScrollingOptOut,
10811089
} = props;
10821090

10831091
const {
@@ -1214,6 +1222,7 @@ class Grid extends React.PureComponent<Props, State> {
12141222
deferredMeasurementCache,
12151223
horizontalOffsetAdjustment,
12161224
isScrolling,
1225+
isScrollingOptOut,
12171226
parent: this,
12181227
rowSizeAndPositionManager: instanceProps.rowSizeAndPositionManager,
12191228
rowStartIndex,
@@ -1546,11 +1555,15 @@ class Grid extends React.PureComponent<Props, State> {
15461555

15471556
_resetStyleCache() {
15481557
const styleCache = this._styleCache;
1558+
const cellCache = this._cellCache;
1559+
const {isScrollingOptOut} = this.props;
15491560

15501561
// Reset cell and style caches once scrolling stops.
15511562
// This makes Grid simpler to use (since cells commonly change).
15521563
// And it keeps the caches from growing too large.
15531564
// Performance is most sensitive when a user is scrolling.
1565+
// Don't clear visible cells from cellCache if isScrollingOptOut is specified.
1566+
// This keeps the cellCache to a resonable size.
15541567
this._cellCache = {};
15551568
this._styleCache = {};
15561569

@@ -1567,6 +1580,10 @@ class Grid extends React.PureComponent<Props, State> {
15671580
) {
15681581
let key = `${rowIndex}-${columnIndex}`;
15691582
this._styleCache[key] = styleCache[key];
1583+
1584+
if (isScrollingOptOut) {
1585+
this._cellCache[key] = cellCache[key];
1586+
}
15701587
}
15711588
}
15721589
}

source/Grid/defaultCellRangeRenderer.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export default function defaultCellRangeRenderer({
1616
deferredMeasurementCache,
1717
horizontalOffsetAdjustment,
1818
isScrolling,
19+
isScrollingOptOut,
1920
parent, // Grid (or List or Table)
2021
rowSizeAndPositionManager,
2122
rowStartIndex,
@@ -109,8 +110,11 @@ export default function defaultCellRangeRenderer({
109110
// However if we are scaling scroll positions and sizes, we should also avoid caching.
110111
// This is because the offset changes slightly as scroll position changes and caching leads to stale values.
111112
// For more info refer to issue #395
113+
//
114+
// If isScrollingOptOut is specified, we always cache cells.
115+
// For more info refer to issue #1028
112116
if (
113-
isScrolling &&
117+
(isScrollingOptOut || isScrolling) &&
114118
!horizontalOffsetAdjustment &&
115119
!verticalOffsetAdjustment
116120
) {

source/Grid/types.js

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export type CellRangeRendererParams = {
2929
deferredMeasurementCache?: Object,
3030
horizontalOffsetAdjustment: number,
3131
isScrolling: boolean,
32+
isScrollingOptOut: boolean,
3233
parent: Object,
3334
rowSizeAndPositionManager: ScalingCellSizeAndPositionManager,
3435
rowStartIndex: number,

0 commit comments

Comments
 (0)