8 React Js performance optimization techniques YOU HAVE TO KNOW!

React Performance Optimization Techniques

React is powerful and feature-friendly, but because of that, we often tend to use too many of its features extensively and end up ignoring the performance downside we could be causing our application. In this article, we will discuss 8 amazing and crucial performance optimization techniques to make your React application super optimized and efficient, so the next time you go deep into using React's cool features, you can keep these techniques in mind.

1. List Virtualization or Windowing

When dealing with a large number of items in a list, rendering all the items at once can lead to slow performance and consume a significant amount of memory. List Virtualization tackles this issue by rendering only a subset of the list items that are currently visible within the viewport, thereby conserving resources. As the user scrolls through the list, the virtualization technique dynamically replaces the rendered items with new ones, keeping the visible portion of the list updated and responsive.

It allows you to efficiently render large lists or tabular data by only rendering the visible portion, recycling components as needed, and optimizing scroll performance. There are different approaches to implementing List Virtualization in React. One popular library that provides this functionality is react-virtualized. By leveraging List Virtualization, React applications can handle massive amounts of data without sacrificing performance or user experience.

2. Lazy Loading Images

Similar to the list virtualization technique mentioned earlier, lazy loading images prevent the creation of unnecessary DOM nodes, thereby boosting performance. With lazy loading, instead of loading all the images on page load, lazy loading allows you to defer or delay the loading of images until they are actually needed or visible to the user.

The concept behind lazy loading is to initially load a placeholder or a small, low-resolution version of the image, typically a small-sized thumbnail or a blurred placeholder. As the user scrolls or interacts with the page, the actual image is loaded dynamically, replacing the placeholder, only when it enters the viewport or is about to become visible.

Lazy loading images in React can be achieved using various libraries and techniques. One popular library is react-lazyload. Another approach is to use the Intersection Observer API, a web API that allows you to efficiently detect when an element enters or exits the viewport. By utilizing this API along with React's useEffect hook, you can implement your custom lazy loading solution for images in React.

3. Memoization

Memoization in React is a technique used to optimize the performance of function components by caching the results of expensive computations or function calls. It is particularly useful when dealing with computationally intensive or frequently called functions that have the same input values, as it helps avoid redundant calculations and improves the overall efficiency of the application.

React.memo

React.memo is a higher-order component used to wrap a purely functional component to prevent re-rendering if the props received in that component never change.

Imagine you have a function component that renders some content based on its props. Normally, whenever the component's parent re-renders, the component itself would also re-render, even if the props haven't changed. This can be inefficient if the rendering process is computationally expensive. By wrapping your component with React.memo, React remembers the result of rendering the component based on its props. If the props haven't changed since the last render, React will reuse the previously rendered result instead of re-rendering the component from scratch. This saves time and resources. In other words, React.memo helps prevent unnecessary re-rendering of components when the props remain the same.

useMemo Hook

The useMemo hook in React is a way to optimize performance by memoizing the result of a function call or an expensive computation. Imagine you have a function that performs a time-consuming calculation. Normally, this function would execute every time your component re-renders, even if the input values haven't changed. This can lead to unnecessary computations and impact performance. By using the useMemo hook, you can tell React to remember the result of the function call and only recalculate it when the input values change. It caches the result and returns it whenever those input values remain the same.

useCallback Hook

The useCallback hook in React is used to optimize performance by memoizing a function instead of memoizing the function result like useMemo does. The useCallback hook is particularly useful when passing functions as props to child components, as it helps prevent unnecessary re-renders.

In React, when a component re-renders, all functions defined within it are recreated. This means that every time a parent component re-renders, the child components that take functions as props receive new instances of those functions from the parent component, even if the functions haven't changed. This can lead to unnecessary re-renders in child components because it thinks its props have changed, thereby impacting performance.

By using the useCallback hook, you can memoize a function and ensure that it remains the same between re-renders as long as the dependencies haven't changed. This way, child components that receive the function as a prop won't re-render unnecessarily when the parent re-renders. useCallback is often used with React.memo because React.memo will ensure that a re-render in the parent component doesn't re-render the child component if the props haven't changed, and useCallback will make sure to stop the recreation of a new function instance during every re-render, allowing that function being passed as a prop to a child component to be the same instance across re-renders, ultimately allowing the child component to not re-render at all even when functions are passed as a callback.

If you found this difficult to understand, then comment down, and I will make a video specifically on this, explaining everything step by step in great detail.

4. Throttling and Debouncing Events

Throttling

Throttling in React is a technique used to limit the number of times a function or an event handler is invoked. It ensures that the function is called at a specified interval, preventing it from being executed too frequently. Throttling allows you to control the rate at which the function is called by setting a minimum time interval between every function invocation. If the function is called multiple times within that interval, only the first invocation is executed, and subsequent invocations are ignored until the interval elapses.

For example, let's say you have a window resize event handler that triggers a function to perform some calculations or update the UI. By applying throttling, you can ensure that the function is called, let's say, every 200 milliseconds. If the user resizes the window rapidly, the function will be invoked at most once every 200 milliseconds instead of being invoked every time the window was resized, reducing the number of computations or UI updates and improving performance.

Debouncing

Debouncing, on the other hand, is also used to limit the number of times a function or an event handler is invoked, but it ensures that the function is called only after a certain period of inactivity, delaying the function execution until a pause in the event stream occurs, such as pausing after typing in an input. This makes it different from throttling, where the function was repeatedly called after a given interval.

Debouncing allows you to postpone the function call until the user has finished typing or a specific time has elapsed since the last event. For example, let's say you have a search input field, and you want to trigger a search API request only when the user has finished typing for a certain duration, like 300 milliseconds. With debouncing, the search function will only be invoked after the user stops typing for 300 milliseconds. If the user continues typing within that interval, the function call will be delayed until the pause occurs. Without debouncing, the function would be called for every keystroke, potentially leading to excessive function calls and unnecessary computations.

5. Code-Splitting

Code splitting in React is a technique used to split a large JavaScript bundle into smaller, more manageable chunks. It helps improve performance by loading only the necessary code for a specific part of an application, rather than loading the entire bundle upfront.

When you develop a React application, all your JavaScript code is typically bundled together into a single file. This file contains all the components, libraries, and other code required for your application to function. However, as your application grows, the bundle size can become quite large, resulting in slower initial load times for your users. Code splitting allows you to divide this single bundle into multiple smaller chunks, which can be loaded selectively based on the current needs of your application. Instead of downloading the entire bundle upfront, only the necessary code is fetched and executed when a user visits a particular page or triggers a specific action.

6. React Fragments

React fragments allow you to group multiple elements together without adding an additional DOM node. They essentially reduce the number of extra tags and prevent extra DOM elements from rendering. When rendering a list of items or a collection of components, you typically need a parent container element to wrap them. In such cases, using a React fragment instead of a regular container element like a div avoids adding an extra node to the DOM, leading to a smaller DOM tree and improved performance.

7. Web Workers

JavaScript is a single-threaded application to handle synchronous executions. While a web page gets rendered, it performs multiple tasks, such as manipulating DOM elements, managing UI interactions, handling API response data, enabling CSS animations, and much more. Now, all of these tasks are taken care of in a single thread.

Web workers are a way to reduce the execution load on the main thread. They allow you to run scripts in the background on a separate thread, different from the main JavaScript thread. They provide a way to execute computationally intensive tasks, perform long-running operations, or handle tasks that may block the main thread without impacting the user interface's responsiveness.

8. useTransition Hook

useTransition is a React Hook that lets you update the state without blocking the UI, thereby increasing the performance of your applications. For example, let's say you have a function in a component that updates both states at the same time upon invoking the function. React is smart enough to know that these state updates happen simultaneously, so it will group them together and perform both state updates together before rendering the component again. This is really nice since it only renders the component once after all the state changes instead of rendering twice, that is once for each state.

But in the case where one of the state updates in the function requires significant computation, React will again try to group them together and perform both state updates together before rendering the component. This causes an issue because one of the state updates requiring higher computation could take up some time to execute, causing the other state update to wait for it to complete, ultimately slowing down the rendering process.

This is where the useTransition hook comes in. The useTransition hook allows us to specify some state updates as less important. These state updates will be executed in parallel with other state updates, but the rendering of the component will not wait for these less important state updates.

Conclusion

So those were 8 performance optimization techniques to supercharge your React application. Keep them in mind before working on your next project.