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 renderRangepull/21833/head
parent
9c371111b7
commit
60d0ed4c01
|
@ -61,14 +61,14 @@ const RoomSubList = React.createClass({
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
// throttle updates to LazyRenderList
|
// throttle updates to LazyRenderList
|
||||||
this._onScroll = _.throttle(
|
// this._onScroll = _.throttle(
|
||||||
this._onScroll, 100,
|
// this._onScroll, 50,
|
||||||
{leading: false, trailing: true},
|
// {leading: false, trailing: true},
|
||||||
);
|
// );
|
||||||
this._updateLazyRenderHeight = _.throttle(
|
// this._updateLazyRenderHeight = _.throttle(
|
||||||
this._updateLazyRenderHeight, 100,
|
// this._updateLazyRenderHeight, 100,
|
||||||
{leading: false, trailing: true},
|
// {leading: false, trailing: true},
|
||||||
);
|
// );
|
||||||
return {
|
return {
|
||||||
hidden: this.props.startAsHidden || false,
|
hidden: this.props.startAsHidden || false,
|
||||||
// some values to get LazyRenderList starting
|
// some values to get LazyRenderList starting
|
||||||
|
@ -307,7 +307,7 @@ const RoomSubList = React.createClass({
|
||||||
{this._getHeaderJsx(isCollapsed)}
|
{this._getHeaderJsx(isCollapsed)}
|
||||||
</div>;
|
</div>;
|
||||||
} else {
|
} 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}>
|
return <div ref="subList" className={subListClasses}>
|
||||||
{this._getHeaderJsx(isCollapsed)}
|
{this._getHeaderJsx(isCollapsed)}
|
||||||
<IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={ this._onScroll }>
|
<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.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const OVERFLOW_ITEMS = 2;
|
import React from "react";
|
||||||
|
|
||||||
export default function LazyRenderList(props) {
|
const OVERFLOW_ITEMS = 20;
|
||||||
const {items, itemHeight, scrollTop, height, renderItem} = props;
|
const OVERFLOW_MARGIN = 5;
|
||||||
|
|
||||||
const firstIdx = Math.max(0, Math.floor(scrollTop / itemHeight) - OVERFLOW_ITEMS);
|
class ItemRange {
|
||||||
const itemsAfterFirst = items.length - firstIdx;
|
constructor(topCount, renderCount, bottomCount) {
|
||||||
const amount = Math.min(Math.ceil(height / itemHeight) + OVERFLOW_ITEMS, itemsAfterFirst);
|
this.topCount = topCount;
|
||||||
const beforeSpace = firstIdx * itemHeight;
|
this.renderCount = renderCount;
|
||||||
const itemsAfter = itemsAfterFirst - amount;
|
this.bottomCount = bottomCount;
|
||||||
const afterSpace = itemsAfter * itemHeight;
|
}
|
||||||
const renderedItems = items.slice(firstIdx, firstIdx + amount);
|
|
||||||
|
|
||||||
return (<div style={{paddingTop: `${beforeSpace}px`, paddingBottom: `${afterSpace}px`}}>
|
contains(range) {
|
||||||
{ renderedItems.map(renderItem) }
|
return range.topCount >= this.topCount &&
|
||||||
</div>);
|
(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 New Issue