diff --git a/package.json b/package.json index 1f9e119..f747b53 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-virtual-list", - "version": "2.0.0", + "version": "2.1.0", "description": "Super simple virtualized list React higher-order component", "main": "dist/VirtualList.js", "directories": { diff --git a/src/VirtualList.js b/src/VirtualList.js index dbea74f..26b5b4c 100644 --- a/src/VirtualList.js +++ b/src/VirtualList.js @@ -2,6 +2,7 @@ import React, { PureComponent, PropTypes } from 'react'; import ReactDOM from 'react-dom'; import getVisibleItemBounds from './utils/getVisibleItemBounds'; +import throttleWithRAF from './utils/throttleWithRAF'; const VirtualList = (options) => (InnerComponent) => { return class vlist extends PureComponent { @@ -40,19 +41,7 @@ const VirtualList = (options) => (InnerComponent) => { // if requestAnimationFrame is available, use it to throttle refreshState if (window && 'requestAnimationFrame' in window) { - const refreshState = this.refreshState; - - this.refreshState = () => { - if (this.isRefreshingState) return; - - this.isRefreshingState = true; - - window.requestAnimationFrame(() => { - refreshState(); - - this.isRefreshingState = false; - }); - }; + this.refreshState = throttleWithRAF(this.refreshState); } }; @@ -64,13 +53,13 @@ const VirtualList = (options) => (InnerComponent) => { this.setState(state); } } - + refreshState() { const { itemHeight, items, itemBuffer } = this.props; this.setStateIfNeeded(this.domNode, this.options.container, items, itemHeight, itemBuffer); }; - + componentDidMount() { // cache the DOM node this.domNode = ReactDOM.findDOMNode(this); @@ -82,7 +71,7 @@ const VirtualList = (options) => (InnerComponent) => { this.options.container.addEventListener('scroll', this.refreshState); this.options.container.addEventListener('resize', this.refreshState); }; - + componentWillUnmount() { // remove events this.options.container.removeEventListener('scroll', this.refreshState); @@ -95,7 +84,7 @@ const VirtualList = (options) => (InnerComponent) => { this.setStateIfNeeded(this.domNode, this.options.container, items, itemHeight, itemBuffer); }; - + render() { const { firstItemIndex, lastItemIndex } = this.state; const { items, itemHeight } = this.props; diff --git a/src/utils/throttleWithRAF.js b/src/utils/throttleWithRAF.js new file mode 100644 index 0000000..3958baf --- /dev/null +++ b/src/utils/throttleWithRAF.js @@ -0,0 +1,15 @@ +export default (fn) => { + let running = false; + + return () => { + if (running) return; + + running = true; + + window.requestAnimationFrame(() => { + fn.apply(this, arguments); + + running = false; + }); + }; +};