Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize scroll performance #130

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
},
"peerDependencies": {
"react": "0.14 || >= 15.0.0-rc.1 < 16",
"react-dom": "0.14 || >= 15.0.0-rc.1 < 16"
"react-dom": "0.14 || >= 15.0.0-rc.1 < 16",
"react-addons-shallow-compare": "0.14 || >= 15.0.0-rc.1 < 16"
},
"devDependencies": {
"cogs": "1.0.7",
Expand Down
46 changes: 33 additions & 13 deletions react-list.es6
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import React, {Component, PropTypes} from 'react';
import ReactDOM from 'react-dom';
import shallowCompare from 'react-addons-shallow-compare';

const {findDOMNode} = ReactDOM;

const isEqualSubset = (a, b) => {
for (let key in a) if (a[key] !== b[key]) return false;
return true;
};

const isEqual = (a, b) => isEqualSubset(a, b) && isEqualSubset(b, a);

const CLIENT_SIZE_KEYS = {x: 'clientWidth', y: 'clientHeight'};
const CLIENT_START_KEYS = {x: 'clientTop', y: 'clientLeft'};
const INNER_SIZE_KEYS = {x: 'innerWidth', y: 'innerHeight'};
Expand All @@ -21,6 +15,12 @@ const SCROLL_START_KEYS = {x: 'scrollLeft', y: 'scrollTop'};
const SIZE_KEYS = {x: 'width', y: 'height'};

const NOOP = () => {};
const requestAnimationFrame =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
const cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;

export default class extends Component {
static displayName = 'ReactList';
Expand Down Expand Up @@ -61,6 +61,7 @@ export default class extends Component {
this.constrain(initialIndex, pageSize, itemsPerRow, this.props);
this.state = {from, size, itemsPerRow};
this.cache = {};
this.rafId = null;
}

componentWillReceiveProps(next) {
Expand All @@ -70,12 +71,12 @@ export default class extends Component {

componentDidMount() {
this.updateFrame = this.updateFrame.bind(this);
window.addEventListener('resize', this.updateFrame);
window.addEventListener('resize', this.updateFrame, {passive: true});
this.updateFrame(this.scrollTo.bind(this, this.props.initialIndex));
}

shouldComponentUpdate(props, state) {
return !isEqual(props, this.props) || !isEqual(state, this.state);
return shallowCompare(this, props, state);
}

componentDidUpdate() {
Expand All @@ -86,6 +87,9 @@ export default class extends Component {
window.removeEventListener('resize', this.updateFrame);
this.scrollParent.removeEventListener('scroll', this.updateFrame);
this.scrollParent.removeEventListener('mousewheel', NOOP);
if (this.rafId !== null) {
cancelAnimationFrame(this.rafId);
}
}

getOffset(el) {
Expand Down Expand Up @@ -219,10 +223,26 @@ export default class extends Component {
prev.removeEventListener('scroll', this.updateFrame);
prev.removeEventListener('mousewheel', NOOP);
}
this.scrollParent.addEventListener('scroll', this.updateFrame);
this.scrollParent.addEventListener('scroll', this.updateFrame, {passive: true});
this.scrollParent.addEventListener('mousewheel', NOOP);
}

setNextState(state, cb) {
if (!requestAnimationFrame) {
this.setState(state, cb);
return;
}

if (this.rafId !== null) {
return;
}

this.rafId = requestAnimationFrame(() => {
this.setState(state, cb);
this.rafId = null;
});
}

updateSimpleFrame(cb) {
const {end} = this.getStartAndEnd();
const itemEls = findDOMNode(this.items).children;
Expand All @@ -239,7 +259,7 @@ export default class extends Component {
if (elEnd > end) return cb();

const {pageSize, length} = this.props;
this.setState({size: Math.min(this.state.size + pageSize, length)}, cb);
this.setNextState({size: Math.min(this.state.size + pageSize, length)}, cb);
}

updateVariableFrame(cb) {
Expand Down Expand Up @@ -271,7 +291,7 @@ export default class extends Component {
++size;
}

this.setState({from, size}, cb);
this.setNextState({from, size}, cb);
}

updateUniformFrame(cb) {
Expand All @@ -288,7 +308,7 @@ export default class extends Component {
this.props
);

return this.setState({itemsPerRow, from, itemSize, size}, cb);
return this.setNextState({itemsPerRow, from, itemSize, size}, cb);
}

getSpaceBefore(index, cache = {}) {
Expand Down
61 changes: 39 additions & 22 deletions react-list.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
(function (global, factory) {
if (typeof define === 'function' && define.amd) {
define(['exports', 'module', 'react', 'react-dom'], factory);
define(['exports', 'module', 'react', 'react-dom', 'react-addons-shallow-compare'], factory);
} else if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
factory(exports, module, require('react'), require('react-dom'));
factory(exports, module, require('react'), require('react-dom'), require('react-addons-shallow-compare'));
} else {
var mod = {
exports: {}
};
factory(mod.exports, mod, global.React, global.ReactDOM);
factory(mod.exports, mod, global.React, global.ReactDOM, global.shallowCompare);
global.ReactList = mod.exports;
}
})(this, function (exports, module, _react, _reactDom) {
})(this, function (exports, module, _react, _reactDom, _reactAddonsShallowCompare) {
'use strict';

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
Expand All @@ -27,17 +27,9 @@

var _ReactDOM = _interopRequireDefault(_reactDom);

var findDOMNode = _ReactDOM['default'].findDOMNode;

var isEqualSubset = function isEqualSubset(a, b) {
for (var key in a) {
if (a[key] !== b[key]) return false;
}return true;
};
var _shallowCompare = _interopRequireDefault(_reactAddonsShallowCompare);

var isEqual = function isEqual(a, b) {
return isEqualSubset(a, b) && isEqualSubset(b, a);
};
var findDOMNode = _ReactDOM['default'].findDOMNode;

var CLIENT_SIZE_KEYS = { x: 'clientWidth', y: 'clientHeight' };
var CLIENT_START_KEYS = { x: 'clientTop', y: 'clientLeft' };
Expand All @@ -50,6 +42,8 @@
var SIZE_KEYS = { x: 'width', y: 'height' };

var NOOP = function NOOP() {};
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
var cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;

var _default = (function (_Component) {
_inherits(_default, _Component);
Expand Down Expand Up @@ -121,6 +115,7 @@

this.state = { from: from, size: size, itemsPerRow: itemsPerRow };
this.cache = {};
this.rafId = null;
}

_createClass(_default, [{
Expand All @@ -137,13 +132,13 @@
key: 'componentDidMount',
value: function componentDidMount() {
this.updateFrame = this.updateFrame.bind(this);
window.addEventListener('resize', this.updateFrame);
window.addEventListener('resize', this.updateFrame, { passive: true });
this.updateFrame(this.scrollTo.bind(this, this.props.initialIndex));
}
}, {
key: 'shouldComponentUpdate',
value: function shouldComponentUpdate(props, state) {
return !isEqual(props, this.props) || !isEqual(state, this.state);
return (0, _shallowCompare['default'])(this, props, state);
}
}, {
key: 'componentDidUpdate',
Expand All @@ -156,6 +151,9 @@
window.removeEventListener('resize', this.updateFrame);
this.scrollParent.removeEventListener('scroll', this.updateFrame);
this.scrollParent.removeEventListener('mousewheel', NOOP);
if (this.rafId !== null) {
cancelAnimationFrame(this.rafId);
}
}
}, {
key: 'getOffset',
Expand Down Expand Up @@ -314,9 +312,28 @@
prev.removeEventListener('scroll', this.updateFrame);
prev.removeEventListener('mousewheel', NOOP);
}
this.scrollParent.addEventListener('scroll', this.updateFrame);
this.scrollParent.addEventListener('scroll', this.updateFrame, { passive: true });
this.scrollParent.addEventListener('mousewheel', NOOP);
}
}, {
key: 'setNextState',
value: function setNextState(state, cb) {
var _this = this;

if (!requestAnimationFrame) {
this.setState(state, cb);
return;
}

if (this.rafId !== null) {
return;
}

this.rafId = requestAnimationFrame(function () {
_this.setState(state, cb);
_this.rafId = null;
});
}
}, {
key: 'updateSimpleFrame',
value: function updateSimpleFrame(cb) {
Expand All @@ -341,7 +358,7 @@
var pageSize = _props5.pageSize;
var length = _props5.length;

this.setState({ size: Math.min(this.state.size + pageSize, length) }, cb);
this.setNextState({ size: Math.min(this.state.size + pageSize, length) }, cb);
}
}, {
key: 'updateVariableFrame',
Expand Down Expand Up @@ -380,7 +397,7 @@
++size;
}

this.setState({ from: from, size: size }, cb);
this.setNextState({ from: from, size: size }, cb);
}
}, {
key: 'updateUniformFrame',
Expand All @@ -402,7 +419,7 @@
var from = _constrain2.from;
var size = _constrain2.size;

return this.setState({ itemsPerRow: itemsPerRow, from: from, itemSize: itemSize, size: size }, cb);
return this.setNextState({ itemsPerRow: itemsPerRow, from: from, itemSize: itemSize, size: size }, cb);
}
}, {
key: 'getSpaceBefore',
Expand Down Expand Up @@ -542,7 +559,7 @@
}, {
key: 'renderItems',
value: function renderItems() {
var _this = this;
var _this2 = this;

var _props8 = this.props;
var itemRenderer = _props8.itemRenderer;
Expand All @@ -555,7 +572,7 @@
for (var i = 0; i < size; ++i) {
items.push(itemRenderer(from + i, i));
}return itemsRenderer(items, function (c) {
return _this.items = c;
return _this2.items = c;
});
}
}, {
Expand Down