Nested Components are an Anti-Pattern in React
- react
- tutorials
To give a definitive answer on why creating components inside another component is bad, is this; It will cause the nested component to be created on every re-render, thus losing its sub-tree and state, leading to performance issues, unexpected behavior, and other bugs. If you’re still interested, we will see why this is the case.
The Problem of Nested Components
You’ve heard it before and perhaps you have done it yourself – declared a component inside another component. Let’s first imagine an example of this situation. For just educative purposes, I’ve created this example component called UnstableComponent
, which then declares a new component inside of its function scope – the ChildComponent
.
Okay, so this is what the headline is all about. Creating a component inside another component is indeed an anti-pattern. This will lead to nasty bugs and confusing code and we’ll see why in a second.
From one of our previous articles about React re-rendering, we learned that the React JSX syntax for components is just a syntax sugar for JavaScript functions.
In our example, calling a component like <ChildComponent/>
is just a way of saying React.createElement(ChildComponent, null, null)
which returns an object representation of the element.
To determine if an element has changed between renders, react uses what is called diffing and reconciliation algorithm. We also scratched this comparison logic in the mentioned article about React re-rendering. A very crude example of what happens:
React will do a comparison of the elements to determine if anything has changed.
Note that React would first check if there is no prevElement
, meaning that the newElement
would be a completely new element. This is just a disclaimer that my example is not 100 % accurate in the implementation details.
Here, in our case, the types are the same; ChildComponent()
,so we are comparing two references to the same object. In JavaScript, functions are just objects, and the objects can be compared. JavaScript will find the referenced memory location and get the value that is stored in that memory slot. From that, it will continue to conclude if the compared objects are the same.
When React tries to perform the comparison prevElement.type !== newElement.type
it will compare two different object references.
Here, React will see a new type on every render and destroy the entire sub-tree’s DOM nodes and state for the ChildComponent
. Therefore, React can’t perform optimization for this kind of code.
The Solution
While the solution might be obvious; Do NOT declare components inside other components. Easy fix would just be to extract the nested component to its own declaration.
This way, the ChildComponent
is not created on every UnstableComponent
re-render and will preserve its sub-tree and state.
It is also good practice to enable the Eslint rule for unstable nested components in your code base. The rule will warn about such oversights.