logo
go back

Optimizing React

Optimizing React

Understanding React

React is a UI library that creates components that leverages a feature the React team calls re-renders. Inside a React application, each component is unique and separate from the other and to each their own state.

This state is the only way React get notified about changes and trigger a re-render. If you're using react to create a web application you're probably using ReactDOM which introduces the concept of V-DOM or a virtual DOM to React.

On each state change the V-DOM that's stored in memory on the first render changes from the current state of the component in the real DOM that's being displayed and then they get compared to each other. If the V-DOM and the DOM don't match, a re-render happens to keep the DOM up to date with the new state, this is called reconciliation.

This only happens if you're changing the state of React and although React is very good at efficiently updating the DOM, this operation can become very expensive if you're building anything that is complex and don't understand how everything works.

How to think about optimization

First I want to point out that not every React application will need optimization so if your application is working as expected and there's no performance issues you can leave it as is but if you like to prematurely optimize everything and to future proof your code the way i like to do it, you can follow this guide.

There are a few key points to this guide:

Folder structure

The way I prefer to structure my React application is to split it into pages inside a folder called pages and each page has its own components folder that is unique to that page. Each component should be a folder. Inside each folder, you find the code for the component itself (could be a lot of smaller components that creates a big one), all the other necessary files that is needed to create the component (assets, hooks, services, types, etc), and a test folder for that unique component testing.

If there's anything that is used across multiple components (buttons, inputs, a component that I needed elsewhere), I tend to put them in the root inside a components folder the same way I do inside the pages folder.

I find this the best way to structure my application as it makes me code faster and don't have to jump around from a folder to another because mostly everything is close to each other and it also makes it easier to go back and understand the code so much better and easier.

Split your components

This part is very simple to understand and implement, basically if you have a big component that has a lot of different parts that don't necessarily communicate with each other in terms of state but are essential to create the same big component, you should separate them into smaller components.

This not only will make your life easier going back to change your code but will also actually help performance by encapsulating the state inside a small component which leads re-renders to happen only inside that small component without re-rendering anything else.

Code splitting and tree shaking

If you're using a third party library, you should tree-shake the imports to decrease the g-zip size of your components. You should also separate the library logic from the component as part of folder structure and decoupling best practices. If you're importing a component from a library, put it in a separate file and then import it in your code.

That being said, you should decrease your dependency for third party libraries, stop being lazy and code the leftPad or isOdd yourself. The least you depend on third party libraries the better you will become at programming and the more you can optimize your code since you own it.

When it comes to big functionalities that will take a lot of time and effort to implement, you should consider using trusted, well-maintained libraries for better security and to reduce the burden of maintaining these functionalities.

Escaping hatches

This section needs a post on its own because of how in depth it can go but I'll give you a general idea of what it is. For example, when using useEffect you should always be careful about infinite loops caused by using an object inside the dependency array. Objects memory references changes on every re-render so it's advised to use primitives but if you need to use an object or a function make sure to memorize the reference using useCallback, useMemo, and memo hooks.

When using these hooks, remember that it only makes a shallow comparison so you need to implement your own function that will make a deep comparison of set dependencies.

useEffect can introduce a lot of bugs and performance issues to your application so make sure to use it correctly. You can also read the documentation to understand why you might not need an effect and the best way to reduce the dependencies of certainuseEffect.

It's not just useEffect that can introduce performance issues, any state or props when used incorrectly can cause unnecessary re-renders so make sure you understand exactly what the code you wrote is doing, what states are changing, and what effect that change has on other components of the application.

Note that if you're using a third party library that is made specifically for React, it's probably using React hooks and React state so they also can re-render your components, make sure to use them in an optimal way.

React built-in optimization methods

It's important to note that React is fast and mostly doesn't need any optimization if you code things correctly. The V-DOM and the way React updates the real DOM are blazingly fast and efficient and with the up-coming React compiler React-Forget you might not even need any of these tips in this guide to optimize performance but it is general good to have a well structure and clean code.

For now if your application is slow, you're probably the culprit as to why it is or you're probably using an unmaintained or old way of building a React application. You should use vite, nextjs, or remix.


See all posts