I recently saw the following tweet from @_georgemoller which posed an interesting question related to component re-rendering in React:
The problem is interesting because, while <SomeComponent />
is a child component of <App />
, <SomeComponent />
does not depend on count
in any way. It does not receive count
as a prop (in fact, <SomeComponent />
doesn't receive any props) and, as a result, is not affected by count
's change in value.
So will <SomeComponent />
be re-rendered every time the value of count
is updated?
Testing environment
For testing, I simply created a new project using create-react-app
. After that I proceeded to delete all the extra stuff not needed for this exercise and put the code in App.js
.
Verifying a re-render
To get to our answer we need a way to verify whether a component was re-rendered or not. The easiest way to do this would be to use the React Developer Tools. These developer tools are available for all major browsers as an extension (except Safari I think).
- After installing the developer tools, right-click anywhere on the page and click Inspect.
- Look for Components and open it.
This tool shows us all the components in our React application and their relation to each other (children are indented under their parent component). Clicking on a component shows more detail like the values of its state and props.
- Click on the settings icon and enable the setting called Highlight updates when components render.
As the name suggests, enabling this setting means that any component that is rendered/re-rendered will be highlighted.
Time to test
This is it. Time to press the button. If <SomeComponent />
is highlighted, it means that <SomeComponent />
is being re-rendered every time count
is being updated.
Interesting! Not only <SomeComponent />
is re-rendered every time the state changes but the text displaying count
's value and and the <button />
are also re-rendered.
Every time the state of a component changes, that component and all of its children are re-rendered.
Just to drive this point home, and emphasize the fact that it does not matter where the value of count
is actually displayed, let's consider some additional scenarios.
Scenario-1
In this scenario, we will pass the value of count to <SomeComponent />
and display it from within <SomeComponent />
. If count
is then updated, the only changing entity is being displayed from within <SomeComponent />
.
App.js
I had to introduce a couple of <p>
tags just to keep everything neat.
Now, practically speaking, the only thing updating the display every time the increment button is pressed is inside <SomeComponent />
on line 20. So how will React handle the re-render?
Once again all components are being re-rendered. We basically have two child components of <App />
(<SomeComponent />
and <button />
) and both of them are clearly being re-rendered. This reinforces the point that:
Every time the state of a component changes, that component and all of its children are re-rendered.
Since the state of count
belongs to <App />
, every time count
changes, <App />
and all of its child components are re-rendered (and the children of those child components as well; I hope that was clear!).
This brings us to the second scenario.
Scenario-2
Since we now know that it makes no difference, let's display the value of count
from within <App />
instead of <SomeComponent />
(just like in the original code). Additionally, I've created a bunch of components just to create a hierarchy.
App.js
By now it should be crystal clear as to what will happen when we change count
.
Scenario-3
For our last scenario, we'll take the code from scenario-2 and move the state from <App />
to <AnotherChildOfSomeComponent />
. And since the data flow in React is from parent to child, and not the other way around, we will display (and update) the value of count
from within <AnotherChildOfSomeComponent />
as well (this makes sense since the whole goal of this exercise is to make count
a part of <AnotherChildOfSomeComponent />
's state).
Time to change count
and see React's rendering in action.
As can be seen, React only re-renders <AnotherChildOfSomeComponent />
and leaves the rest alone.
Conclusion
Kindly allow me to say it again...
Every time the state of a component changes, that component and all of its children are re-rendered.
Be very mindful of which component handles the state in a React application. If you put it in the root component (like in the original problem), your whole application will re-render every time that state changes. This can have a serious impact on your application's performance.
For example, imagine a bunch of data-driven child components that query various APIs. Every time those components are rendered they'll hit those APIs. Now that might be something you intend, but it just might be a side-effect of keeping state in the wrong component.
Top comments (11)
This isn't entirely true because React compares each child and bails out of re-rendering if a child is strictly equal.
That is actually not true as you can see in the examples. This behavior can be prevented though either by using memoization or the
shouldComponentUpdate
method (assuming the state can't be moved to another component of course).The
shouldComponentUpdate
method returnstrue
by default. This is why the default behavior of React is to re-render all child components on state change.Not true, huh?
I've provided an example code, have you even checked it? Does
SomeComponent
re-renders when you click the button? So annoying...I stand corrected. But can I ask, why are you re-assigning the component to a variable and then using that variable to render the component? Because this seems to be why
SomeComponent
is not re-rendering.It does re-render when you plug it in directly with
<SomeComponent />
even though its still not changing whencount
updates.I'm not re-assigning anything.
someComponent
is a React element and<SomeComponent/>
is a function call that returns a React element. These are two different things from JavaScript standpoint.If you have
A = () => ({})
thenA() !== A()
but whena = A()
thena === a
. That's the gist of it.Thanks for this post, really helpful.
Appreciate your feedback.
This is not true i think. I believe you are talking about 're evaluation' and not 're rendering'. Here the SomeComponent is indeed re evaluated as mentioned in the post,but it is not re rendered, as React sees no new changes to the element that requires a DOM update.
Great post!
And we also can use React.memo() for prevent unnecessary rerenders.
Absolutely. But you'd have to memoize every child component. In the case where the state lives in the root component (like in the original question), you'll have to memoize every component of the app! 😄