Do you want to request a feature or report a bug?
feature
What is the current behavior?
There's very bad performance if I only want to update a very small part of an array with the setState method, which causes the render method is triggered every time I update the array, and the entire array is traversed again and again . As the array gets larger and larger, react takes more resource of the computer, especially CPU. In such cases, calling the DOM method ParentNode.append with the ref feature of react makes a lot better performance than the React's setState way. However, calling the DOM method a lot out of React is not the graceful solution.
I have come across such scenario a few times. Here is an example written in pseudocode.
class DataList extends React.Component {
constructor (props) {
super(props);
this.state = {
dataSourceList = [],
};
}
getData = () => {
// Some data is being fetched from the server
// This piece of data is assigned to a variable, say `newData`
this.setState({
dataSourceList: _.deepClone(this.state.dataSourceList).push(newData)
});
}
render () {
return (
<div>
{
this.state.dataSourceList.map(v => {
return <DataItem key={v.id} value={v.value} />
})
}
</div>
);
}
}
Every time the getData method is invoked, the render method and map method are triggered in order to update the React element array. This does not bother much performance if you fetch these data manually by clicking some button to trigger the getData method.
However, there is a scenario where this costs a lot of CPU resource. I wrote a "streaming log" component with which a line of log was printed to the screen every time the "websocket" receive a message from the server. One piece of message renders the counterpart component DataItem above. Sometimes, the server sends hundreds of thousands of messages within one second, which causes the render method and map method has to be triggered thousands of times accordingly. This behavior even makes the current web page occupy 100% of the CPU and my computer got frozen until the updating process was finished.
In order to solve this issue, I have to use the web API's append method to mount a DOM node carrying those messages directly in to the DOM node of the "steaming log" component' wrapper div. This solution avoids the render part which is under the complexity of O(n) (n is the length of the array). A snippet of pseudocode is as below
class DataList extends React.Component {
getData = () => {
// Some data is being fetched from the server
// This piece of data is assigned to a variable, say `newData`
dataItem = document.createElement('div');
dataItem.innerHTML = newData.value;
this.containerNode.append(dataItem);
}
render () {
return (
<div ref={node => this.containerNode = node} />
);
}
}
Even though using the DOM method append improves the behavior greatly, it is not always the elegant solution. Is it possible that we have a _"React"_ way to update a very small part of the array, or just to pushing an element to the array to be rendered?
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
It is not a bug. It probably be a flaw of the React architecture
What is the expected behavior?
The expected behavior is we may update the array in the above scenario with the original React API rather than directly calling the DOM node's method
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
Present version: v16.x
Browser: All
OS: All
It did not work in previous versions either.
@gaearon
@yuqingc maybe this react-virtualized will help you ?
rendering 1000 array item at a time will surely cause your problem
You also should use a key on your list items. Rendering a lot of list items frequently is not a strong suit of React, but it's also generally not very performant with the vanilla DOM depending on how big the list is. As mentioned we recommend using windowing Technics to only render the visible items, via react-virtualized, react-list or similar
@samundrak @jquense Thanks
Another thing to keep in mind is that you can make DataItem inherit PureComponent. If props are shallowly equal then rendering of unchanged items will be skipped.