DEV Community

Shameel Uddin
Shameel Uddin

Posted on

⚛ The Terrible React Anti-Pattern You Must Avoid

Introduction

In the world of React development, it's not uncommon to come across patterns that might seem convenient at first but can quickly turn into performance bottlenecks. One such anti-pattern that you should steer clear of is defining React components inside other components. This seemingly innocent practice can be one of the biggest performance killers in your application, resulting in slow rendering times and strange behaviors. In this blog post, we'll dive into the details of this anti-pattern and explore how to avoid it for a smoother and more efficient React application.

The Anti-Pattern: Defining Components Inside Components

The anti-pattern in question can be best illustrated through a code example:

const Component = () => {
  const ChildComponent = () => <div>Content</div>;
  return <ChildComponent />;
};
Enter fullscreen mode Exit fullscreen mode

In this code snippet, we define ChildComponent within Component. While this may seem harmless, it leads to some undesirable consequences.

The Problem with This Approach

  1. Recreating ChildComponent: When Component is re-rendered, React will create a new instance of ChildComponent every time. This means that the "previous" ChildComponent will be removed, and the "next" one will be mounted. This behavior is terrible for performance, as it introduces unnecessary overhead and slows down your application.

  2. State Disappearance: If ChildComponent has its own state, it will lose that state every time it is recreated during a re-render of Component. This can lead to unexpected and erroneous behaviors in your application.

The Solution: Defining Components Outside Components

To avoid this performance-killing anti-pattern, it's essential to define your components outside the scope of other components. Here's how you should structure your code:

const ChildComponent = () => <div>Content</div>;

const Component = () => {
  return <ChildComponent />;
};
Enter fullscreen mode Exit fullscreen mode

By defining ChildComponent outside of Component, you ensure that it won't be recreated with every re-render of Component. Instead, it behaves as a normal component and re-renders only when its props or state change.

Benefits of This Approach

  1. Improved Performance: Defining components outside other components reduces unnecessary re-creations, resulting in improved rendering performance. Your application will render faster and provide a smoother user experience.

  2. Consistent State: If ChildComponent has state, it will maintain that state across re-renders, ensuring consistent behavior and avoiding potential bugs.

Conclusion

In the world of React development, it's crucial to be mindful of anti-patterns that can harm your application's performance. Defining React components inside others is one such anti-pattern that should be avoided at all costs. By adhering to best practices and defining components outside of other components, you can ensure that your application performs optimally and provides a consistent user experience. So, remember, if you want to avoid applications with horrible performance and weird behaviors, never define React components inside others. Your users will thank you for it!

Happy coding! 🎉💻✨

Follow me for more such content:
LinkedIn: https://www.linkedin.com/in/shameeluddin/
Github: https://github.com/Shameel123

Top comments (9)

Collapse
 
scastiel profile image
Sebastien Castiel

Note that, without defining components inside components, it’s perfectly okay to define a function that returns JSX inside a component:

function MyList() {
  const { some, nice, values } = getSomeNiceValues()

  function generateItemJSX(item) {
    return <span className={`${some} ${nice} ${values}`}>{item.name}</span>
  }

  return (
    <ul>
      <li key={item.id}>{generateItemJSX(item)}</li>
    </ul>
  )
}
Enter fullscreen mode Exit fullscreen mode

Sometimes you want to make the code easier to read and maintain, and extracting part of the logic into a function makes sense, but it isn’t worth it to create a new component.

With a function inside the component, you take advantage of inner values defined inside the components. From React’s point of view, it’s still one component.

Collapse
 
etienneburdet profile image
Etienne Burdet • Edited

I would actually call functions that retrun JSX but are not components an anti-pattern too: the component will recreate the function and JSX each time and you won't benefit of all the props diffing React does, while really, it's a component.

99% of the time it is totally "worth" making a component, there is no drawback to it and the list/item you give actually is a common exemple of splitting components

function Item (item) {
  const { some, nice, values } = getSomeNiceValues()
  return (
   <span className={`${some} ${nice} ${values}`}>{item.name}</span>
  )
}

function MyList(items) {
  return (
    <ul>
        {items.map(item => <Item item={item} />)}
    </ul>
  )
}
Enter fullscreen mode Exit fullscreen mode

Or if the value have to come from the list:

 

function Item ({ item, some, nice, value }) {
  return (
   <span className={`${some} ${nice} ${values}`}>{item.name}</span>
  )
}

function MyList(items) {
  return (
    <ul>
        {items.map(item => <Item item={item} {... getSomeNiceValues()} />)}
    </ul>
  )
}
Enter fullscreen mode Exit fullscreen mode

Both ways, you avoid reacreating lots of stuff, you scope things for the adde cost of uppercasing the function and moving it out.

Collapse
 
brense profile image
Rense Bakker

Actually no, that's not okay, unless you use the useCallback hook to memoize that function (which is really just a react component). Defining a function component like this, without memoization inside another component, will cause the component to be redefining on every render which can be expensive if the state changes often and it opens you up to unexpected behavior when you pass the component as children to a provider component for example which will cause the entire tree under that provider to always rerender. There's no difference between your approach and just defining the function component outside of your component.

Collapse
 
rajaerobinson profile image
Rajae Robinson

Great post. I have actually rarely seen this anti-pattern in codebases. I guess that's a good thing. I wrote an article recently outlining React JS best practices. It's definitely worth a read

Collapse
 
barrymichaeldoyle profile image
Barry Michael Doyle

You're right! This is definitely a pattern worth staying clear of. I've come across code written like this and it can mask some dodgy bugs. Better to write the component separately from the get go to avoid crazy side effects from biting you later.

Collapse
 
dsaga profile image
Dusan Petkovic

Thanks for the info, I've never done this personally but have seen it in some codebases.

Btw. what is your take on having multiple components per file when creating a React App, usually in the component based architecture we only have one component per file, but sometimes its overkill to create a file for a very small component..

Collapse
 
polaroidkidd profile image
Daniel Einars

I see this so often. In addition to the performance problems that come with it (they might be minor for static components) it makes the coffee difficult to reason about.

Collapse
 
tyagu profile image
Thiyagu

The mistake i have been doing for long time. Thank you for sharing.

Collapse
 
shameel profile image
Shameel Uddin

You are welcome =)