Optimizing React Applications: Top 5 Commonly Used React Optimization Techniques

Optimizing React Applications: Top 5 Commonly Used React Optimization Techniques

If you have been building web applications using React lately, probably you might be looking out for the articles, on optimizing, them. I thought of writing some of the common approaches, used by most of the experienced developers working on React. Without wasting time let’s dive into it.

Top 5 Commonly Used React Optimization Mantras:

1. Understanding Performance Bottlenecks

Before diving into optimizations, it’s essential to identify performance bottlenecks. React DevTools and the built-in Performance Profiler are invaluable for pinpointing slow components and rendering issues. Profiling helps you understand where time is being spent and which components need attention.

2. Efficient State Management, to Avoid Unnecessary Re-renders

One of the most common performance pitfalls in React is unnecessary re-renders. Here are some strategies to mitigate this:

  • Memoization with React.memo: This higher-order component prevents functional components from re-rendering unless their props change.
const MyComponent = React.memo(({ data }) => {
  // Component logic
});
  • Using useMemo and useCallback: These hooks cache expensive calculations and functions, respectively, to avoid re-execution on every render.
const expensiveCalculation = useMemo(() => computeExpensiveValue(data), [data]);
const memoizedCallback = useCallback(() => handleEvent(data), [data]);
  • Using Context API Sparingly: While the Context API is powerful for passing data through the component tree without prop drilling, it can lead to unnecessary re-renders if not used carefully. This issue, I have faced a lot in the early days of my career. To optimize:

  • Split Contexts: Instead of a single context for multiple pieces of state, use multiple contexts to ensure only the relevant parts of the tree re-render.

const UserContext = React.createContext();
const ThemeContext = React.createContext();

3. Optimizing Component Rendering

  • Code Splitting: Code splitting allows you to split your code into smaller chunks, which are then loaded on demand. This reduces the initial load time of your application.

  • React.lazy and Suspense: These features enable lazy loading of components.

const LazyComponent = React.lazy(() => import('./LazyComponent'));

const App = () => (
  <Suspense fallback={<div>Loading...</div>}>
    <LazyComponent />
  </Suspense>
);
  • Virtualization: When dealing with large lists or tables, rendering all items at once can be costly. Virtualization renders only the visible items, significantly improving performance.

  • react-window and react-virtualized: These libraries provide efficient virtualization solutions.

import { FixedSizeList as List } from 'react-window';

const MyList = ({ items }) => (
  <List
    height={400}
    itemCount={items.length}
    itemSize={35}
    width={300}
  >
    {({ index, style }) => <div style={style}>{items[index]}</div>}
  </List>
);

4. Optimizing API Calls and Data Handling

Debouncing and Throttling

For applications that frequently interact with APIs, debouncing and throttling can prevent excessive requests and improve performance.

  • Debounce: Limits the rate at which a function is executed, ensuring it runs only after a certain delay since the last invocation.
const handleChange = debounce((value) => {
  // API call logic
}, 300);
  • Throttle: Ensures a function is executed at most once in a specified period.
const handleScroll = throttle(() => {
  // Scroll event logic
}, 200);

Efficient Data Fetching

  • Use SWR or React Query: These libraries handle caching, background fetching, and synchronization, making data fetching more efficient and resilient.
import useSWR from 'swr';

const fetcher = url => fetch(url).then(res => res.json());

const MyComponent = () => {
  const { data, error } = useSWR('/api/data', fetcher);

  if (error) return <div>Error</div>;
  if (!data) return <div>Loading...</div>;

  return <div>{data.message}</div>;
}

5. Optimizing Rendering of Large Data Sets

When dealing with large data sets, such as rendering charts with a million data points, special techniques are required to ensure performance:

Web Workers

Offload heavy computations to Web Workers to avoid blocking the main thread. I this use implementation a lot cause lot many times we have to deal with enormous amount of data.

Which would have to go through some computation. That may block, the main thread of the UI and make the interface jittery. Checkout the example below to implement it:

// worker.js
self.onmessage = function(event) {
  const result = expensiveComputation(event.data);
  self.postMessage(result);
};


// Main component
const worker = new Worker('worker.js');

worker.onmessage = function(event) {
  setData(event.data);
};

worker.postMessage(largeDataSet);

Hope, you find this article useful. There are also, few more optimization techniques, that might not be used by most of the developers commonly. But, yes will be writing a separate article on it in depth.

If you like my work, follow to never miss an update. Also, do support me by liking, commenting and sharing the article. These small things, keep me motivated to contribute such content 😌.

Did you find this article valuable?

Support codersk36 by becoming a sponsor. Any amount is appreciated!