I would like to ask what is the correct way to fast render > 10000 items in React.
Suppose I want to make a checkboxList which contain over dynamic 10000 checkbox items.
I make a store which contain all the items and it will be used as state of checkbox list.
When I click on any checkbox item, it will update the corresponding item by action and so the store is changed.
Since store is changed so it trigger the checkbox list update.
The checkbox list update its state and render again.
The problem here is if I click on any checkbox item, I have to wait > 3 seconds to see the checkbox is ticked. I don't expect this as only 1 checkbox item need to be re-rendered.
I try to find the root cause. The most time-consuming part is inside the checkbox list render method, related to .map which create the Checkbox component to form componentList.. But actually only 1 checkbox have to re-render.
The following is my codes. I use ReFlux for the flux architecture.
CheckboxListStore
The Store store all the checkbox item as map. (name as key, state (true/false) as value)
const Reflux = require('reflux');
const Immutable = require('immutable');
const checkboxListAction = require('./CheckboxListAction');
let storage = Immutable.OrderedMap();
const CheckboxListStore = Reflux.createStore({
listenables: checkboxListAction,
onCreate: function (name) {
if (!storage.has(name)) {
storage = storage.set(name, false);
this.trigger(storage);
}
},
onCheck: function (name) {
if (storage.has(name)) {
storage = storage.set(name, true);
this.trigger(storage);
}
},
onUncheck: function (name) {
if (storage.has(name)) {
storage = storage.set(name, false);
this.trigger(storage);
}
},
getStorage: function () {
return storage;
}
});
module.exports = CheckboxListStore;
CheckboxListAction
The action, create, check and uncheck any checkbox item with name provided.
const Reflux = require('reflux');
const CheckboxListAction = Reflux.createActions([
'create',
'check',
'uncheck'
]);
module.exports = CheckboxListAction;
CheckboxList
const React = require('react');
const Reflux = require('reflux');
const $ = require('jquery');
const CheckboxItem = require('./CheckboxItem');
const checkboxListAction = require('./CheckboxListAction');
const checkboxListStore = require('./CheckboxListStore');
const CheckboxList = React.createClass({
mixins: [Reflux.listenTo(checkboxListStore, 'onStoreChange')],
getInitialState: function () {
return {
storage: checkboxListStore.getStorage()
};
},
render: function () {
const {storage} = this.state;
const LiComponents = storage.map((state, name) => {
return (
<li key = {name}>
<CheckboxItem name = {name} />
</li>
);
}).toArray();
return (
<div className = 'checkbox-list'>
<div>
CheckBox List
</div>
<ul>
{LiComponents}
</ul>
</div>
);
},
onStoreChange: function (storage) {
this.setState({storage: storage});
}
});
module.exports = CheckboxList;
CheckboxItem Inside onChange callback, I call the action to update the item.
const React = require('react');
const Reflux = require('reflux');
const $ = require('jquery');
const checkboxListAction = require('./CheckboxListAction');
const checkboxListStore = require('./CheckboxListStore');
const CheckboxItem = React.createClass({
mixins: [Reflux.listenTo(checkboxListStore, 'onStoreChange')],
propTypes: {
name: React.PropTypes.string.isRequired
},
getInitialState: function () {
const {name} = this.props;
return {
checked: checkboxListStore.getStorage().get(name)
};
},
onStoreChange: function (storage) {
const {name} = this.props;
this.setState({
checked: storage.get(name)
});
},
render: function () {
const {name} = this.props;
const {checked} = this.state;
return (
<div className = 'checkbox' style = {{background: checked ? 'green' : 'white'}} >
<span>{name}</span>
<input ref = 'checkboxElement' type = 'checkbox'
onChange = {this.handleChange}
checked = {checked}/>
</div>
);
},
handleChange: function () {
const {name} = this.props;
const checked = $(this.refs.checkboxElement).is(':checked');
if (checked) {
checkboxListAction.check(name);
} else {
checkboxListAction.uncheck(name);
}
}
});
module.exports = CheckboxItem;
There are a few approaches you can take:
storage.toArray().map(...)
(so that you aren't creating an intermediate map), or even better, make and empty array and then do storage.forEach(...) adding the elements with push - much faster. But the React diffing algorithm is still going to have to diff 10000 elements, which is never going to be fast, however fast you make the code that generates the elements.Add a custom shouldComponentUpdate to CheckboxList:
shouldComponentUpdate:function(nextProps, nextState) {
var storage = this.state.storage;
var nextStorage = nextState.storage;
if (storage.size !== nextStorage.size) return true;
// check item names match for each index:
return !storage.keySeq().equals(nextStorage.keySeq());
}
Beyond the initial render, you can significantly increase rendering speed of large collections by using Mobservable. It avoids re-rendering the parent component that maps over the 10.000 items unnecessarily when a child changes by automatically applying side-ways loading. See this blog for an in-depth explanation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With