Avoid Nested Declared React Components
Introduction
While React excels at simplifying UI development, itâs not foolproof against misuse by even experienced developers. One common mistake that even experienced developers make is declaring React components within other components. This practice, while seemingly innocuous, can lead to unexpected behavior and performance issues. In this post, weâll explore why this is problematic and how to avoid it.
The Problem: Nested Declared React Components
Letâs start with a simple example to illustrate the issue:
function ParentComponent() {
function NestedComponent() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
return (
<div>
<h1>Parent Component</h1>
<NestedComponent />
</div>
);
}
At first glance, this code might seem fine. It works, and it might even pass a code review. However, it harbors a subtle but significant problem.
The Issue with State
When you declare a component inside another component, that inner component is recreated on every render of the parent component. This means that any state within the nested component is reset on each render.
To see this in action, check out this CodeSandbox. Try the following:
- Increase the count in both the âBadâ and âGoodâ lists.
- Add an item to both lists (click the âAdd Itemâ button).
Youâll notice that the count resets in the âBadâ list, but in the âGoodâ list, the state persists.
Why This Happens
When you declare a component inside another component, itâs not really a component in the React sense. Itâs just a function. On each re-render of the parent component, a new reference to this function is created. This means:
- The âcomponentâ loses its identity between renders.
- Any state or effects associated with this âcomponentâ are reset.
- Reactâs reconciliation process canât optimize renders for this âcomponentâ.
The Solution: Declare Components Separately
The solution is simple: declare your components separately. Hereâs how you can refactor the previous example:
function NestedComponent() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
function ParentComponent() {
return (
<div>
<h1>Parent Component</h1>
<NestedComponent />
</div>
);
}
By declaring NestedComponent outside of ParentComponent, we ensure that:
- The component maintains its identity across renders.
- State and effects work as expected.
- React can properly optimize renders for this component.
Best Practices
- Separate File: If your file is getting large (over 200-300 lines), consider moving child components to a new file.
- Same File: For smaller components, itâs fine to keep them in the same file as long as theyâre not nested.
- Higher-Order Components: If you need to create components dynamically, consider using higher-order components or render props instead of nesting component declarations.
The Rules of Hooks
This issue is closely related to the Rules of Hooks in React. Hooks (like useState and useEffect) rely on a stable component identity to work correctly. When you nest component declarations, youâre inadvertently breaking these rules.
Further Reading
- Donât call a React function component by Kent C. Dodds
- Video explanation of this issue
Conclusion
While it might be tempting to declare components within other components for the sake of organization or scoping, itâs a practice that should be avoided. By keeping your component declarations at the top level, you ensure that your components behave predictably and perform optimally.
Remember, in React development, clarity and predictability are key. By following these guidelines, youâll create more maintainable and efficient React applications.