make LazyRenderList stateful for better performance
it only rerenders when visible range it would render based on the props gets OVERFLOW_MARGIN(5) items from the current renderRange
This commit is contained in:
parent
9c371111b7
commit
60d0ed4c01
2 changed files with 81 additions and 22 deletions
|
@ -61,14 +61,14 @@ const RoomSubList = React.createClass({
|
|||
|
||||
getInitialState: function() {
|
||||
// throttle updates to LazyRenderList
|
||||
this._onScroll = _.throttle(
|
||||
this._onScroll, 100,
|
||||
{leading: false, trailing: true},
|
||||
);
|
||||
this._updateLazyRenderHeight = _.throttle(
|
||||
this._updateLazyRenderHeight, 100,
|
||||
{leading: false, trailing: true},
|
||||
);
|
||||
// this._onScroll = _.throttle(
|
||||
// this._onScroll, 50,
|
||||
// {leading: false, trailing: true},
|
||||
// );
|
||||
// this._updateLazyRenderHeight = _.throttle(
|
||||
// this._updateLazyRenderHeight, 100,
|
||||
// {leading: false, trailing: true},
|
||||
// );
|
||||
return {
|
||||
hidden: this.props.startAsHidden || false,
|
||||
// some values to get LazyRenderList starting
|
||||
|
@ -307,7 +307,7 @@ const RoomSubList = React.createClass({
|
|||
{this._getHeaderJsx(isCollapsed)}
|
||||
</div>;
|
||||
} else {
|
||||
const items = this.props.list.concat(this.props.extraTiles);
|
||||
const items = this.props.list; //.concat(this.props.extraTiles);
|
||||
return <div ref="subList" className={subListClasses}>
|
||||
{this._getHeaderJsx(isCollapsed)}
|
||||
<IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={ this._onScroll }>
|
||||
|
|
|
@ -14,20 +14,79 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
const OVERFLOW_ITEMS = 2;
|
||||
import React from "react";
|
||||
|
||||
export default function LazyRenderList(props) {
|
||||
const {items, itemHeight, scrollTop, height, renderItem} = props;
|
||||
const OVERFLOW_ITEMS = 20;
|
||||
const OVERFLOW_MARGIN = 5;
|
||||
|
||||
const firstIdx = Math.max(0, Math.floor(scrollTop / itemHeight) - OVERFLOW_ITEMS);
|
||||
const itemsAfterFirst = items.length - firstIdx;
|
||||
const amount = Math.min(Math.ceil(height / itemHeight) + OVERFLOW_ITEMS, itemsAfterFirst);
|
||||
const beforeSpace = firstIdx * itemHeight;
|
||||
const itemsAfter = itemsAfterFirst - amount;
|
||||
const afterSpace = itemsAfter * itemHeight;
|
||||
const renderedItems = items.slice(firstIdx, firstIdx + amount);
|
||||
class ItemRange {
|
||||
constructor(topCount, renderCount, bottomCount) {
|
||||
this.topCount = topCount;
|
||||
this.renderCount = renderCount;
|
||||
this.bottomCount = bottomCount;
|
||||
}
|
||||
|
||||
return (<div style={{paddingTop: `${beforeSpace}px`, paddingBottom: `${afterSpace}px`}}>
|
||||
{ renderedItems.map(renderItem) }
|
||||
</div>);
|
||||
contains(range) {
|
||||
return range.topCount >= this.topCount &&
|
||||
(range.topCount + range.renderCount) <= (this.topCount + this.renderCount);
|
||||
}
|
||||
|
||||
expand(amount) {
|
||||
const topGrow = Math.min(amount, this.topCount);
|
||||
const bottomGrow = Math.min(amount, this.bottomCount);
|
||||
return new ItemRange(
|
||||
this.topCount - topGrow,
|
||||
this.renderCount + topGrow + bottomGrow,
|
||||
this.bottomCount - bottomGrow,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default class LazyRenderList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const renderRange = LazyRenderList.getVisibleRangeFromProps(props).expand(OVERFLOW_ITEMS);
|
||||
this.state = {renderRange};
|
||||
}
|
||||
|
||||
static getVisibleRangeFromProps(props) {
|
||||
const {items, itemHeight, scrollTop, height} = props;
|
||||
const length = items ? items.length : 0;
|
||||
const topCount = Math.max(0, Math.floor(scrollTop / itemHeight));
|
||||
const itemsAfterTop = length - topCount;
|
||||
const renderCount = Math.min(Math.ceil(height / itemHeight), itemsAfterTop);
|
||||
const bottomCount = itemsAfterTop - renderCount;
|
||||
return new ItemRange(topCount, renderCount, bottomCount);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
const state = this.state;
|
||||
const range = LazyRenderList.getVisibleRangeFromProps(props);
|
||||
// only update state if the new range isn't contained by the old anymore
|
||||
if (!state.renderRange || !state.renderRange.contains(range.expand(OVERFLOW_MARGIN))) {
|
||||
this.setState({renderRange: range.expand(OVERFLOW_ITEMS)});
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
const itemsChanged = nextProps.items !== this.props.items;
|
||||
const rangeChanged = nextState.renderRange !== this.state.renderRange;
|
||||
return itemsChanged || rangeChanged;
|
||||
}
|
||||
|
||||
render() {
|
||||
const {itemHeight, items, renderItem} = this.props;
|
||||
|
||||
const {renderRange} = this.state;
|
||||
const paddingTop = renderRange.topCount * itemHeight;
|
||||
const paddingBottom = renderRange.bottomCount * itemHeight;
|
||||
const renderedItems = items.slice(
|
||||
renderRange.topCount,
|
||||
renderRange.topCount + renderRange.renderCount,
|
||||
);
|
||||
|
||||
return (<div style={{paddingTop: `${paddingTop}px`, paddingBottom: `${paddingBottom}px`}}>
|
||||
{ renderedItems.map(renderItem) }
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue