Skip to content

Commit a11f5ae

Browse files
committed
grid scrolling improvements
1 parent 58aed2d commit a11f5ae

File tree

4 files changed

+68
-36
lines changed

4 files changed

+68
-36
lines changed

Diff for: src/Canvas.js

+56-19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ const React = require('react');
22
const ReactDOM = require('react-dom');
33
const joinClasses = require('classnames');
44
const PropTypes = React.PropTypes;
5-
const shallowEqual = require('fbjs/lib/shallowEqual');
65
const ScrollShim = require('./ScrollShim');
76
const Row = require('./Row');
87
const cellMetaDataShape = require('./PropTypeShapes/CellMetaDataShape');
@@ -31,14 +30,16 @@ const Canvas = React.createClass({
3130
columns: PropTypes.oneOfType([PropTypes.object, PropTypes.array]).isRequired,
3231
cellMetaData: PropTypes.shape(cellMetaDataShape).isRequired,
3332
selectedRows: PropTypes.array,
34-
rowKey: React.PropTypes.string
33+
rowKey: React.PropTypes.string,
34+
rowScrollTimeout: React.PropTypes.number
3535
},
3636

3737
getDefaultProps() {
3838
return {
3939
rowRenderer: Row,
4040
onRows: () => {},
41-
selectedRows: []
41+
selectedRows: [],
42+
rowScrollTimeout: 0
4243
};
4344
},
4445

@@ -62,25 +63,12 @@ const Canvas = React.createClass({
6263
},
6364

6465
componentWillReceiveProps(nextProps: any) {
65-
let scrollbarWidth = this.getScrollbarWidth();
66-
let shouldUpdate = !(nextProps.visibleStart > this.state.displayStart
67-
&& nextProps.visibleEnd < this.state.displayEnd)
68-
|| nextProps.rowsCount !== this.props.rowsCount
69-
|| nextProps.rowHeight !== this.props.rowHeight
70-
|| nextProps.columns !== this.props.columns
71-
|| nextProps.width !== this.props.width
72-
|| nextProps.cellMetaData !== this.props.cellMetaData
73-
|| !shallowEqual(nextProps.style, this.props.style);
74-
75-
if (shouldUpdate) {
66+
if (nextProps.displayStart !== this.state.displayStart
67+
|| nextProps.displayEnd !== this.state.displayEnd) {
7668
this.setState({
77-
shouldUpdate: true,
7869
displayStart: nextProps.displayStart,
79-
displayEnd: nextProps.displayEnd,
80-
scrollbarWidth: scrollbarWidth
70+
displayEnd: nextProps.displayEnd
8171
});
82-
} else {
83-
this.setState({shouldUpdate: false, scrollbarWidth: scrollbarWidth});
8472
}
8573
},
8674

@@ -112,8 +100,28 @@ const Canvas = React.createClass({
112100
this.appendScrollShim();
113101
let {scrollTop, scrollLeft} = e.target;
114102
let scroll = {scrollTop, scrollLeft};
103+
// check how far we have scrolled, and if this means we are being taken out of range
104+
let scrollYRange = Math.abs(this._scroll.scrollTop - scroll.scrollTop) / this.props.rowHeight;
105+
let scrolledOutOfRange = scrollYRange > (this.props.displayEnd - this.props.displayStart);
106+
115107
this._scroll = scroll;
116108
this.props.onScroll(scroll);
109+
// if we go out of range, we queue the actual render, just rendering cheap placeholders
110+
// avoiding rendering anything expensive while a user scrolls down
111+
if (scrolledOutOfRange && this.props.rowScrollTimeout > 0) {
112+
let scrollTO = this.state.scrollingTimeout;
113+
if (scrollTO) {
114+
clearTimeout(scrollTO);
115+
}
116+
// queue up, and set state to clear the TO so we render the rows (not placeholders)
117+
scrollTO = setTimeout(() => {
118+
if (this.state.scrollingTimeout !== null) {
119+
this.setState({scrollingTimeout: null});
120+
}
121+
}, this.props.rowScrollTimeout);
122+
123+
this.setState({scrollingTimeout: scrollTO});
124+
}
117125
},
118126

119127
getRows(displayStart: number, displayEnd: number): Array<any> {
@@ -166,6 +174,12 @@ const Canvas = React.createClass({
166174
},
167175

168176
renderRow(props: any) {
177+
if (this.state.scrollingTimeout !== null) {
178+
// in the midst of a rapid scroll, so we render placeholders
179+
// the actual render is then queued (through a timeout)
180+
// this avoids us redering a bunch of rows that a user is trying to scroll past
181+
return this.renderScrollingPlaceholder(props);
182+
}
169183
let RowsRenderer = this.props.rowRenderer;
170184
if (typeof RowsRenderer === 'function') {
171185
return <RowsRenderer {...props}/>;
@@ -176,7 +190,30 @@ const Canvas = React.createClass({
176190
}
177191
},
178192

193+
renderScrollingPlaceholder(props: any): ?ReactElement {
194+
// here we are just rendering empty cells
195+
// we may want to allow a user to inject this, and/or just render the cells that are in view
196+
// for now though we essentially are doing a (very lightweight) row + cell with empty content
197+
let styles = {
198+
row: {height: props.height, overflow: 'hidden'},
199+
cell: {height: props.height, position: 'absolute'},
200+
placeholder: {backgroundColor: 'rgba(211, 211, 211, 0.45)', width: '60%', height: Math.floor(props.height * 0.3)}
201+
};
202+
return (
203+
<div key={props.key} style={styles.row} className="react-grid-Row">
204+
{this.props.columns.map(
205+
(col, idx) =>
206+
<div style={Object.assign(styles.cell, {width: col.width, left: col.left})} key={idx} className="react-grid-Cell">
207+
<div style={Object.assign(styles.placeholder, {width: Math.floor(col.width * 0.6)})}></div>
208+
</div>
209+
)}
210+
</div>
211+
);
212+
},
213+
179214
renderPlaceholder(key: string, height: number): ?ReactElement {
215+
// just renders empty cells
216+
// if we wanted to show gridlines, we'd need classes and position as with renderScrollingPlaceholder
180217
return (
181218
<div key={key} style={{height: height}}>
182219
{this.props.columns.map(

Diff for: src/Grid.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ const Grid = React.createClass({
3131
onColumnResize: PropTypes.func,
3232
onSort: PropTypes.func,
3333
cellMetaData: PropTypes.shape(cellMetaDataShape),
34-
rowKey: PropTypes.string.isRequired
34+
rowKey: PropTypes.string.isRequired,
35+
rowScrollTimeout: PropTypes.number
3536
},
3637

3738
mixins: [
@@ -91,6 +92,7 @@ const Grid = React.createClass({
9192
cellMetaData={this.props.cellMetaData}
9293
rowOffsetHeight={this.props.rowOffsetHeight || this.props.rowHeight * headerRows.length}
9394
minHeight={this.props.minHeight}
95+
rowScrollTimeout={this.props.rowScrollTimeout}
9496
/>
9597
</div>
9698
:

Diff for: src/Viewport.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ const Viewport = React.createClass({
2121
onScroll: PropTypes.func,
2222
minHeight: PropTypes.number,
2323
cellMetaData: PropTypes.shape(cellMetaDataShape),
24-
rowKey: PropTypes.string.isRequired
24+
rowKey: PropTypes.string.isRequired,
25+
rowScrollTimeout: PropTypes.number
2526
},
2627

2728
onScroll(scroll: {scrollTop: number; scrollLeft: number}) {
@@ -70,15 +71,14 @@ const Viewport = React.createClass({
7071
expandedRows={this.props.expandedRows}
7172
columns={this.props.columnMetrics.columns}
7273
rowRenderer={this.props.rowRenderer}
73-
visibleStart={this.state.visibleStart}
74-
visibleEnd={this.state.visibleEnd}
7574
displayStart={this.state.displayStart}
7675
displayEnd={this.state.displayEnd}
7776
cellMetaData={this.props.cellMetaData}
7877
height={this.state.height}
7978
rowHeight={this.props.rowHeight}
8079
onScroll={this.onScroll}
8180
onRows={this.props.onRows}
81+
rowScrollTimeout={this.props.rowScrollTimeout}
8282
/>
8383
</div>
8484
);

Diff for: src/addons/grids/ReactDataGrid.js

+6-13
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ const ReactDataGrid = React.createClass({
7373
onGridSort: React.PropTypes.func,
7474
onDragHandleDoubleClick: React.PropTypes.func,
7575
onRowSelect: React.PropTypes.func,
76-
rowKey: React.PropTypes.string
76+
rowKey: React.PropTypes.string,
77+
rowScrollTimeout: React.PropTypes.number
7778
},
7879

7980
getDefaultProps(): {enableCellSelect: boolean} {
@@ -83,7 +84,8 @@ const ReactDataGrid = React.createClass({
8384
rowHeight: 35,
8485
enableRowSelect: false,
8586
minHeight: 350,
86-
rowKey: 'id'
87+
rowKey: 'id',
88+
rowScrollTimeout: 0
8789
};
8890
},
8991

@@ -98,12 +100,6 @@ const ReactDataGrid = React.createClass({
98100
return initialState;
99101
},
100102

101-
componentWillReceiveProps: function(nextProps: ReactDataGridProps) {
102-
if (nextProps.rowsCount === this.props.rowsCount + 1) {
103-
this.onAfterAddRow(nextProps.rowsCount + 1);
104-
}
105-
},
106-
107103
onSelect: function(selected: SelectedType) {
108104
if (this.props.enableCellSelect) {
109105
if (this.state.selected.rowIdx !== selected.rowIdx
@@ -224,10 +220,6 @@ const ReactDataGrid = React.createClass({
224220
}
225221
},
226222

227-
onAfterAddRow: function(numberOfRows: number) {
228-
this.setState({selected: { idx: 1, rowIdx: numberOfRows - 2 }});
229-
},
230-
231223
onToggleFilter() {
232224
this.setState({ canFilter: !this.state.canFilter });
233225
},
@@ -523,7 +515,8 @@ const ReactDataGrid = React.createClass({
523515
onViewportDragStart={this.onDragStart}
524516
onViewportDragEnd={this.handleDragEnd}
525517
onViewportDoubleClick={this.onViewportDoubleClick}
526-
onColumnResize={this.onColumnResize}/>
518+
onColumnResize={this.onColumnResize}
519+
rowScrollTimeout={this.props.rowScrollTimeout}/>
527520
</div>
528521
</div>
529522
);

0 commit comments

Comments
 (0)