Tips for writing better React components
A guide to writing clean and readable React components
React has re-defined the way we build user interfaces. Since its first release in 2013 by Facebook, it has grown rapidly over the past 7 years. According to NPM trends, out of the topmost JavaScript front-end libraries and frameworks, React has managed to secure first place for many many years.
If you have been writing your front-end applications in React for a while, ask yourself this question:
Have I been writing clean React components in a readable and performant manner?
I believe that best practices to write a component may vary based on the application requirement. There are no clear-cut rules or standards defining the best way to write React components. In this article, we will look at some methods to make your components cleaner and more performant.
Let’s dive in.
1. Class vs. Functional components
If you started learning React before 2018, you would have had to make a choice whether to write a functional component or a class component. Generally, we make that choice based on whether we want our component to be stateless or stateful. However, with the introduction of React hooks in 2018, this entire concept changed. Now you can write stateful functional components. If that is the case, do we still need to write class components?
In my opinion,
Writing functional components is better.
There is a reason behind this. Functional components are much lighter than class components. Class components come with a bunch of pre-loaded functionality inside them, whether we need it or not. For Functional components, we have the option of adding in functionality if required.
Moreover, functional components are easy to test, since they are pure functions.
Point to note:
There are some edge cases where you will have to use class components based on your application requirement. One such scenario is if you need the functionality of
getSnapshotBeforeUpdate,getDerivedStateFromErrororcomponentDidCatch. As of now, there are no hooks developed to obtain these functionalities.
As of recently, it feels like even the React team’s focus is more towards functional components and hooks. They haven’t announced any plans of deprecating class components. However, the new developments in React focus more on functional components.
2. Naming is important
Always make sure that you name your component. Naming a component makes it easier to search through a large codebase and trace errors using React Dev Tools.
// This is an unnamed component
export default () => <div>...</div>// This is a named component
export const HeaderInfo => () => { return <div>...</div> )
Giving a relevant name to your component is as important as having a name in the first place. You should always name the component after the functionality it is responsible for. For example, if there is a form in your application that is used to capture customer data, a suitable name for the component would be CustomerForm instead of merely naming it Form .
3. Avoid unnecessary re-renders — Memoize!
Whether you write class components or functional components, it is extremely important to take measures to avoid unnecessary re-renders. If all the child components re-render (even if their data remains unchanged), when the parent component re-renders, your application performance will reduce drastically.
How do we do this?
If your component is a class component, use PureComponent. If your component is a functional component use React.memo() . This is called, memoization of components.
A pure component or a memoized functional component has the shouldComponentUpdate function in-built. Therefore, even if their parent component re-renders, if the data used in them does not change, they won’t re-render. The only instance in which it will re-render is when the title prop changes.
4. Strive for single responsibility
Your application interface is a combination of multiple components. One crucial fact to keep in mind when writing a React component is to make sure that the component is responsible for doing one thing and one thing ONLY.
Write small, focused components.
For example, if you have a Navigation Bar, a Form and a Footer in your interface, a clean way of organizing your components would be;
Page Container (Parent Component)
|____ Nav Bar Component (Child #1)
|____ Form Component (Child #2)
|____ Input Component (Grandchild #1)
|____ Checkbox Component (Grandchild #2)
|____ Textarea Component (Grandchild #3)
|____ Button Component (Grandchild #4) |____ Footer Component (Child #3)
A structure such as this will make sure that your component will handle only one responsibility. Encapsulate complex logic in nested components. For example, all the form-related logic should reside in the Form Component instead of the Page Container. As a result, a single component will not be very long and will be easily readable and maintainable.
Another good practice is to maintain a list of shared components when necessary. For example, if you have many forms in your application and the submit button is used in all of those, we can extract this to a shared component. Based on which form it is used in, you can pass the necessary props to achieve the required functionality. This is more commonly known as DRYing your code (DRY — Don’t repeat yourself).
5. Keep state business logic away from the UI
Having UI logic and state business logic crammed together within the component makes a component less reusable and more difficult to test. Moreover, if you need to change your state management mechanism at some point, this will be impossible if you have to refactor all components. Let’s have a look at an example.
The above component has state business logic embedded within the component, via the onClick function. At a glance, it is a bit difficult to read. How can we extract the state mutation and place it out of the component? It’s very simple — we use a custom hook.
With a custom hook, the component looks cleaner and more readable.
6. Organize your component well
There are few things you can do to organize your component better. First of all, you should have separate files for each component. Having nested render() functions are not advised as it makes the code harder to read.
Let’s look at a few more suggestions that help to keep the component clean.
Separate style definitions
If you have any styles applicable to your component elements, it is better to define them in a separate file ( styles.scss ) within the component folder itself. You can then import the styles file to your component index.js . Even though styles are related to the UI, keeping your styles in a separate file will make the component cleaner and maintainable.
The practice I have gotten used to is to define a class name for a particular style and include it in the HTML elements. The styling applicable to the class name will be defined in the styles.scss file.
Point to note: Separation of styles and scoping styles can also be achieved using CSS Modules.
Have separate files for constants and helpers
Keeping a component under 150–200 lines is always a good practice to improve maintainability. Therefore, all constant definitions can go into a separate file such as component.constants.js or simply constants.js if it is within the component folder. Helper functions and utility functions that are shared between components also can go to a separate file(s).
7. Manage component properties
Most developers don’t pay attention to the props they pass into the components, whether they have default values, how many props are passed, etc. For a small scale project, this might not be a major issue. However, as your application grows and especially in enterprise software, I have seen how props become unmanageable. There are few practices that we can follow to avoid such issues.
Destructure props
When props are destructured, it improves readability. Further, destructuring props will avoid any repetitions.
Assign default props when destructuring
There are two ways to define default props. The approach you choose will be subjective. There is no correct or incorrect way to do this.
- Define default props at the end of the file
- Define default props when destructuring
In my opinion, defining props at the place where destructuring happens, makes it easier to read the code, because you don’t have to jump up and down the file to find the default value of a property.
Destructuring props and assigning default props itself provides documentation to the component. Any developer is able to easily read and understand the structure of the component. While both these methods are correct, for a lengthy component, defining props at the bottom of the file might be a bit inconvenient when the file is read.
Don’t pass too many props
If you are passing too many props into your component, that means your component is doing too many things. As a result, it becomes harder to maintain and read. If a lot of props are being passed into a component, extract them and modularize the component into smaller chunks.
8. Use functions for event handlers
I have noticed that most developers write an event handler functionality within the event handler itself. For example,
const HeaderInfo = ({ title }) => (
<div>
<h1>{title}</h1>
<p>{subTitle}</p>
<input onClick={(e) => console.log(e.target.value)} />
</div>
);However, if the event handler functionality is complex, this makes it hard to read the component. Instead, the better practice would be to extract it to a function.
When you have the handler function in this manner, the component tree looks much cleaner.
Point to note:
If you are writing class components, it is better to define the function outside the
render()method. As a result, you can avoid declaring the function every time the component re-renders. The function will only be declared once. This improves the performance of the application.
Summary
We discussed a number of methods in which you can make your component more readable, performant, and clean. However, it should be noted that these are not the only methods to optimize a component. There are many other methods out there as well. For example, if you want to conditionally render child components within a parent component, it is better to extract it to a function.
Likewise, ways of optimizing a component are endless. I have highlighted a few important methods that you can start practicing immediately if you haven’t already. I hope that this will help you write better React components. The practices you choose to follow will be subjective. If you come across more interesting ways to optimize React components, don’t forget to leave a comment.
Thanks for reading!

