I know about PureComponent, but from the deep thought, I got confused about it.
ShouldComponentUpdate function is invoked when state or props are changed. So, we definitely know that something in state or props are changed if ShouldComponentUpdate is invoked. And those changes should be reflected in component view also. If state or props are not changed, shouldComponentUpdate is never called.
The PureComponent provides shallow comparison between old state or props and new state or props and if they are identical, it does not render. But we already know that the fact that shouldComponentUpdate is called means that new state or props are not same as old state or props.
Logically, I am confused about it so I got to post this question. Could you please give some advice for it?
NOTE: this is my understanding, so others please feel free to comment any issues/corrections
You've made an incorrect assumption in your question. You think for a plain React.Component that if nothing has changed in state/props then shouldComponentUpdate is never called and it won't re-render. That's not true, for example if a plain component's parent re-renders and that parent passes props to the child, the child will re-render too. Even if those prop values that it's passing are actually the same (you can test this by putting a console.log in the child's render() function). However, even though the child will re-render, nothing will actually change (in terms of the DOM) since the props are the same. So technically it did an unnecessary re-render.
In a plain component, shouldComponentUpdate always returns true (assuming you haven't explicitly defined it). So if the props/state are the same it will always re-render, it's just that if there aren't any pertinent differences, the DOM won't change in any way. However, in a PureComponent it will actually do a shallow comparison of the new state/props, and only trigger a re-render if they've changed. The idea is that it implements a shouldComponentUpdate method for you.
For components that don't often want to change, doing this shallow comparison (which is relatively quick and cheap) is better than constantly just re-rendering regardless of any changes. To summarise, pure components prevent unnecessary re-renders by doing comparisons of state/props before re-rendering, and making sure they're actually different.
Related
I think the fact is that, when a parent component re-renders in React, in general, all the children components re-render as well.
I did one experiment to confirm:
https://codesandbox.io/s/currying-pine-r16rzi
return (
<div>
<div>Time now is {timeNow.toLocaleTimeString()}</div>
<TheTimeNow /><TheTimeNow />
</div>
);
This parent component re-renders, and <TheTimeNow /> has no change in props value (it has none), but it re-renders anyway and shows the updated time.
I think it is not the same to say, that React actually change the DOM, as I think the mechanism is that React use the previous virtual DOM to compare with the new virtual DOM, and update only the minimal actual DOM node as on document.documentElement as needed.
This can be seen if you use Google Chrome and do Inspect Element on the time statement: only the time portion changes and the other English words stay the same. Compare to this where all the nodes changes if it is plain JavaScript: https://jsfiddle.net/8sj2ugae/1/ when you do Inspect Element.
However, can't we argue that, if a child is <Foo /> and since there is no props passed to it, then <Foo /> really should not have changed, and it is wasteful to call Foo()? (if Foo is a functional component, or call the render() method if it is a class component.)
Now since all children re-renders, that means all their children also get re-rendered. Repeat this rule recursively, that would mean the whole subtree gets re-rendered.
My question is: is it true that they should not need to re-render if their props didn't change, or are there other factors that make them needing a re-render really?
The answer is yes, there is no need to rerender. But in order to know this we need to compare a new props with a previous props. And this operation is not free.
Also if we do a shallow comparison we are increasing the probability of bugs. Someone might be expecting rerender after a deep props update.
Should React.memo be the deault behaviour? Will it improve the performance in most cases? We don't know. There is no evidence for that. More on this in Mark Erikson's post.
Here is a great article by Dan Abramov: Before You memo(). I consider two points regarding your question:
In the future compiler might decide when to memoize a component.
Component rendering doesn't mean that all it's children will be rerendered.
Here is an example:
const ParentComponent = ({ children }) => {
const [state, setState] = useState(0);
return <div>{children}</div>
}
If we update the state children will not be rerendered. React just uses a previous value. Here is an article by Kent C. Dobbs about this optimization technique.
One last thing. React.memo and useMemo are different. The first one is for component memoization and the second one is a hook for expensive calculations. There are also shouldComponentUpdate for class components and PureComponent which compare not only props but also a state.
Hopefully the answer and links will shed some light on this topic.
I'm new to react. I'm going through their docs to understand how react works. So it was mentioned that when state/props/setState() changes/called, react rerenders the entire component.
Also, I read that react elements are immutable and they are rendered only when there is a change. So when react tries to render a component it actually traverses through all the elements checks for differences and renders only those elements whose data is changed. It won't simply re-render the entire component.
Am I right regarding this? Or is my understanding wrong?
I read that React elements are immutable and they are rendered only when there is a change.
Saying that React elements are immutable is not true, you need to treat them as immutable, especially component's state and props.
So when React tries to render a component it actually traverses through all the elements checks for differences and renders only those elements whose data is changed.
The default behaviour is to render all sub tree when the parent rerendered by calling setState (which triggers Reconciliation on component's sub tree).
So saying it will render components on "data change" is not true, they will rerender anyway by default, even if the "data" didn't change.
On saying "data is changed" we mean on props change (shallow comparison by default, use memoization to change it).
We can use key prop to help with the reconciliation process.
You are right, react re-renders the component when props or state changes.
That being said, when a child component received new props, react does not check if the props have changed when you use React.Component, it just re-renders even if you pass same props again.
In order to make components render only if they receive different props you can use React.PureComponent in case of class components or you can wrap the component with React.memo() in case of functional components.
I believe a clearer way to summarize when React components re-render would be:
React components re-render when, and only when:
Their state changes (via setState() or useState())
They are passed new props;
Their parent component re-renders.
Caveats:
You must update state correctly for the component to re-render, i.e. via setState() or useState(). If state changes via other, "illegal" means, such as directly accessing state, React won't notice, and won't re-render the component.
React props are read-only, so when we say "when a component's props change," we really mean when the component is passed props with changed values. The props should not be mutated within the component.
If you use useMemo() or React.memo(), then a child component will only re-render when the parent component re-renders if the props it receives have changed.
It's important to distinguish between re-rendering the virtual DOM and updating the actual DOM. Just because a component re-renders doesn't mean it's updated the DOM. When a component re-renders, React compares the new version to the previous, and only updates the actual DOM if something has changed 1, 2.
Nothing has made this clearer for me than this cheat sheet by A. Sidorenko.
Edit:
Essentially yes any state change will trigger a re-render. This includes the component where the state change was initiated and all its children. For instance, if your composition is:
A > B > C
If the state for A is updated then A, B, and C will get re-rendered. There are ways to prevent re-rendering subcomponents (e.g. memo, useMemo) so I point you the cheat sheet referred to above for the complete details.
I'm trying to optimize the performance of app. I've found that the issue is that one of my components re-renders on a condition that I set and it contains a significant amount of data. I wanted to know if it were possible to not re-render the whole component, but to only re-render the element that has changed?
I've already tried using PureComponent, but that did not have enough control for me. I'm currently using shouldComponentUpdate.
By default every time a parent component is re rendered all the children nodes will be rendered as well, if you know for sure that a given component will always render the same content given the same props then you could turn it into a PureComponent, a pure component performs a shallow comparison obj1 === obj2 in props and only re-render when a change occurs. If you need a more complex comparison logic then shouldComponentUpdate is the way to go:
shoudComponentUpdate(prevProps, prevState){
if(prevProps.items.length !== this.props.items.length) return true
return false
}
By default this method always returns true, unless you explicitly tells it not to. The functional version of shouldComponentUpdate is React.memo. But keep in mind that comparisons take time, and the amount of processing required to perform a comparison grows with the complexity. Use it carefully.
I would suggest breaking out the piece that needs to be updated into it's own component and use React.memo or PureComponent like you were saying. This might also help.
React partial render
componentWillRecieveProps is deprecated and getDerivedStateFromProps is being advocated to use.
But componentWillRecieveProps gets called when the props get changed but getDerivedStateFromProps is getting called on any state change even.
My requirement is to reset state on props change. So how would I get to know if the props really got changed.
I am not in favor of storing the props value in state as it would unnecessarily make the state bulkier and I still not have given state management(Redux or Context API) a thought
Please advise
how would I get to know if the props really got changed.
Compare previous prop with new prop.
For simplest use case, just use componentDidUpdate(), with prop comparing:
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
According to React Doc, it is OK to perform side effect or setState() inside componentDidUpdate().
And setState() can cause re-rendering, which affects component performance. When performance in this case is actually a thing, read on: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#when-to-use-derived-state
I have been reading the React docs and came across shouldComponentUpdate(). My understanding is that everytime setState() is called, a re-render of that component will be updated.
My question is that If the value to be updated is the SAME as the current state value, would this trigger a re-render event? or I would have to manually checks for the current value and value to be updated in shouldComponentUpdate()
The official React documentation states:
The default behavior is to re-render on every state change...
https://reactjs.org/docs/react-component.html#shouldcomponentupdate
This means that by default, render() will be executed if any of a component's state or props values changes.
You can override this default behavior using shouldComponentUpdate(). Here's an example that only updates if a state value changes.
shouldComponentUpdate(nextProps, nextState) {
return this.state.someValue !== nextState.someValue;
}
Note: this example completely ignores props. So, any changes to props will not trigger render().
Adding more to #Jyothi's answer regarding implementing shouldComponentUpdate() to skip unnecessary re-renders, in React 15.3 they introduced a new concept PureComponent. From reactjs docs
The difference between them is that React.Component doesn’t implement
shouldComponentUpdate(), but React.PureComponent implements it with a
shallow prop and state comparison.
This allows to skip unnecessary calls of render in class components by just implementing PureComponent instead of the usual Component. There are a few caveats with PureComponent though, from the docs about React.PureComponent’s shouldComponentUpdate():
... only shallowly compares
the objects. If these contain complex data structures, it may produce
false-negatives for deeper differences.
... skips prop updates for the whole component subtree. Make sure all the
children components are also “pure”.
Usage of PureComponent can in some cases improve performance of your app. Moreover, it enforces you to keep state and props objects as simple as possible or even better, immutable, which might help simplify the app structure and make it cleaner.
I dont know if I understood your question correctly but react only re renders when there is difference between virtual dom and real dom.
And as Jyothi mentioned in his answer that render method will be called irrespective of the value passed in the set state function but rerendering will depend on what this render method returns.
In functional components, calling setState() with the equal value won't cause a rendering, while in a class component it does: https://dev.to/sunflower/reactjs-if-it-is-setting-a-state-with-the-same-value-will-the-component-be-re-rendered-5g24
Note that we're just talking about virtual (React) renderings here. Brower-rendering won't happen in any case - i.e. neither in the functional component nor in the class component - as long as the state (or to be more precise: the effective DOM) doesn't change.