Let\'s say I have a list of 1000 items. And I rendering it with React, like this:
class Parent extends React.Component {
render() {
// this.state.list is a
you have to compare a item with your items
UpdateComment=(comment)=>{
let objIndex = this.state.comments.findIndex((obj => obj.id === comment.id));
let comments = [...this.state.comments];
comments[objIndex].content = comment.content
comments[objIndex].idea = comment.idea
this.setState({comments});
}
When you change your data, react default operation is to render all children components, and creat virtual dom to judge which component is need to be rerender.
So, if we can let react know there is only one component need to be rerender. It can save times.
You can use shouldComponentsUpdate
in your list component.
If in this function return false, react will not create vitual dom to judge.
I assume your data like this [{name: 'name_1'}, {name: 'name_2'}]
class Item extends React.Component {
// you need judge if props and state have been changed, if not
// execute return false;
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.name === this.props.name) return false;
return true;
}
render() {
return (
<li>{this.props.name}</li>
)
}
}
As react just render what have been changed component. So if you just change one item's data, others will not do render.
EDIT:
My inital suggestions only addressed possible efficiency improvements to rendered lists and did not address the question about limiting the re-rendering of components as a result of the list changing.
See @xiaofan2406's answer for a clean solution to the original question.
Libraries that help make rendering long lists more efficient and easy:
React Infinite
React-Virtualized
One way to avoid looping through the component list every render would be to do it outside of render function and save it to a variable.
class Item extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return this.props.item != nextProps.item;
}
render() {
return <li>{this.props.item}</li>;
}
}
class List extends React.Component {
constructor(props) {
super(props);
this.items = [];
this.update = this.update.bind(this);
}
componentWillMount() {
this.props.items.forEach((item, index) => { this.items[index] = <Item key={index} item={item} /> });
}
update(index) {
this.items[index] = <Item key={index} item={'item changed'} />
this.forceUpdate();
}
render() {
return <div>
<button onClick={() => { this.update(199); }}>Update</button>
<ul>{this.items}</ul>
</div>
}
}
There are a few things you can do:
NODE_ENV=production npm run build
or similar. ReactJS performs a lot of safety checks when NODE_ENV
is not set to production, such as PropType checks. Switching these off should give you a >2x performance improvement for React rendering, and is vital for your production build (though leave it off during development - those safety checks help prevent bugs!). You may find this is good enough for the number of items you need to support.firstRendered
/lastRendered
props to your List state (that's first inclusive and last exclusive of course). In List.render
, render a filler blank div
(or tr
if applicable) of the correct height (firstRendered * itemHeight
), then your rendered range of items [firstRendered, lastRendered)
, then another filler div with the remaining height ((totalItems - lastRendered) * itemHeight
). Make sure you give your fillers and items fixed keys. You then just need to handle onScroll on the scrollable div, and work out what the correct range to render is (generally you want to render a decent overlap off the top and bottom, also you want to only trigger a setState to change the range when you get near to the edge of it). A crazier alternative is to render and implement your own scrollbar (which is what Facebook's own FixedDataTable does I think - https://facebook.github.io/fixed-data-table/). There are lots of examples of this general approach here https://react.rocks/tag/InfiniteScroll You can do it by using any state management library, so that your Parent
doesn't keep track of this.state.list
=> your List
only re-renders when new Item
is added. And the individual Item
will re-render when they are updated.
Lets say you use redux
.
Your code will become something like this:
// Parent.js
class Parent extends React.Component {
render() {
return <List />;
}
}
// List.js
class List extends React.Component {
render() {
var list = this.props.list.map(item => {
return <Item key={item.key} uniqueKey={item.key} />;
});
return <div>{list}</div>;
}
}
const mapStateToProps = (state) => ({
list: getList(state)
});
export default connect(mapStateToProps)(List);
// Item.js
class Item extends React.Component {
shouldComponentUpdate() {
}
render() {
}
}
const mapStateToProps = (state, ownProps) => ({
item: getItemByKey(ownProps.uniqueKey)
});
export default connect(mapStateToProps)(Item);
Of course, you have to implement the reducer and the two selectors getList
and getItemByKey
.
With this, you List
re-render will be trigger if new elements added, or if you change item.key
(which you shouldn't)