In this tutorial, you will learn several takeaways that will help you write better components in React.js. Before we get into the implementation details, it would be good to define what I would consider a badly defined component. First, when you load a page, if your component starts to feel a little sluggish, or, the whole page starts to freeze, then it's likely that you've done something wrong. Adding one instance of your component to a page is OK, but what happens if you add two or more? If you have encountered these types of issues, and want to learn some new techniques to solve them, then read on.
One common mistake people make when building components, is not paying enough attention to how many times that component gets re-rendered unnecessarily. When you build a React application built upon components, you implicitly build a hierarchical component tree. When a component passes some props to another component, it is called the Owner. An owner may have one or more children. When the owner component updates a prop, this change can propagate down all of its child components, forcing them to re-render. If the component is at the very page of the hierarchy, this can cause a lot of updates. To make things worse, if you don't write your components with referential equality, in the majority of situations, these updates are completely unnecessary.
Out of the box, React helps alleviate this problem, with PureComponent. When you use inherit from PureComponent for instance, React knows how to optimize the state update inside event handlers and it batches the operation for better performance. In earlier versions of React, if you created a stateless component, you could end up with a less performant component accidentally, as a lot of your the components that get created at the bottom of the component hierarchy can get re-rendered over and over. PureComponent optimization is why it's a good practice to always try and extend from PureComponent.
If you have performance issues, then Chrome dev tools can be a lifesaver. If you look in the performance tab and record the performance of your page, you will be able to see a flame-graph of how your component behaves. Another useful tip is to use the React develop tool extension for Chrome to make sure that there are no props that are unexpectedly changing and causing components to render unnecessarily.
When building React components, a very common bit of advice that you will see mentioned everywhere is to follow the single responsibility pattern. Each component does one specific thing, well - this is typically achieved via higher order components.
If you write components that try to do too many things, then you'll end up with lots of props being passed into your component. Having too many props is undesirable, as it will make your code harder to understand, maintain and change later on.
Building high order components that compose functionality into child components, will keep your components small and minimise the number of props each component will require. The less props a component has the less likely it will be re-rendered unnecessarily.
Think About The Type of Props You Pass Into A Component
When passing props down, you also want to consider the prop types that are being passed into it. Take the snippet below:
In the code above we have two issues with the props being passed in MyComponent:
- In objectPassedDown we pass a new object MyComponent whenever the parent is run. As the object is new, referential equality will be broken within MyComponent, so the components render method will be re-run every-time
- In functionPassedDown a new function is passed into MyComponent whenever the parent is run. As the object is new, referential equality will be broken within MyComponent, so the components render method is run
Breaking referential equality can cause performance issues in your application. One way to fix the issues above is to store the object being passed into objectPassedDown within state instead.
Write Performant Components
To prevent breaking referential equality between components, there are a few techniques you can follow. First, instead of passing a new object each time, you should pass in a property store in state, like so:
Within MyComponents, you can then add some checks in shouldComponentUpdate() to add some basic component memorisation, like so:
Use Hooks!!! Instead of relying on PureComponent and passing everything within a prop, you could use a hook and compose the data in that you need and bind it to state. This will also prevent your component to re-render unnecessarily.
Tips n Tricks
As you can see there are a few different techniques you can follow when you start building your components that will make them work a little quicker and also result in less clunky and more declarative code. As well as ensuring referential equality between components, you should also think about:
- Be very strict about not passing unneeded props into your components. The more props you pass in, the higher the chance your component will get re-rendered by a parent
- Props cannot and should not be modified
- Only store values in state that are used within the render method
- Immutable.js is a useful tool to ensure you do not accidentally mute your objects to prevent accidental renders