Speed up your React Components with flattened states and React.PureComponent
More often than not I see React Components written like this:
class MyComponent extends React.Component {
constructor(props) {
this.state = {
data: props.data /* Where data is an object */
};
}
Nesting objects in the state make it difficult to detect if anything really did change in props.data
. And if we don't both checking if data has really changed we are possibly rendering our Component (and thus the children below it), for no good reason. Consider this:
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
setInterval(() => {
this.setState({
count: 0
});
}, 1000)
}
render() {
console.log(this.state); // Added this only to illustrate the component behaviour. Avoid console.logs in render() method at all times in production.
return (
<div className="App">
Unrelated div text
</div>
);
}
}
Yes! this.state.count
is not even being referenced in the render()
. Yet, on checking on the console, you'll find render()
methods being called repeated even if the data never really changed. Imagine such a component with hundreds of children. Each one, triggering render for absolutely no good reason.
Just make the above to a React PureComponent
- class MyComponent extends React.Component {
+ class MyComponent extends React.PureComponent {
You won't find repeated rendering this time. If this was a large component, you avoided tons of re-renders.
How it works
React.PureComponent
does a simple shallow check on all its props and state behind the scenes.
if (ctor.prototype && ctor.prototype.isPureReactComponent) {
return (
!shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
);
}
shallowEqual
checks every property on the old object against the new one. (For the curious lot, the source can be found here )
Back to our original problem: nested objects in state
Try running the following on CodeSandbox.
// Assume App was rendered as <App data={data} /> This has already been done for you in the sandbox link above.
class App extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
data: props.data
}
}
componentDidMount() {
setInterval(() => {
this.setState({
data: {
foo: 1,
bar: 2
}
})
}, 1000)
}
render() {
console.log(this.state)
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
}
Logs flooding the console. Which means render was called repeatedly.
Now make the following two changes.
- this.state = {
- data: props.data
- }
+ this.state = {
+ ...props.data
+ }
and in componentDidMount
- setInterval(() => {
- this.setState({
- data: {
- foo: 1,
- bar: 2
- }
- });
- }, 1000);
+ setInterval(() => {
+ this.setState({
+ foo: 1,
+ bar: 2
+ });
+ }, 1000);
A sandbox has been created for you already if you'd like to use that. No more repeated component re-renders. Think about how many components re-renders have been avoided this way when data hasn't really changed
So use make your state shallow and enjoy the benefits of React.PureComponent