React performance optimization techniques reactjs 18
August 23rd 2024

Optimizing performance in a React application, especially with React 18, involves a combination of techniques that focus on reducing re-renders, improving load times, and making the most of React’s new features. Below are some key performance optimization techniques tailored for React 18:

 

1. Use React 18 Features

 

Concurrent Rendering:

React 18 introduces concurrent rendering, allowing React to prepare multiple versions of the UI at the same time. This helps in making the UI more responsive by rendering parts of the UI in the background without blocking the main thread.

Use useTransition to defer updates that don’t need to happen immediately.

Example:

const [isPending, startTransition] = useTransition();

function handleClick() {
  startTransition(() => {
    // Transitioned state update
    setState(newState);
  });
}

Automatic Batching:

React 18 automatically batches state updates, even inside promises, setTimeout, or event handlers, reducing unnecessary renders.

Example:

function handleClick() {
  setCount(c => c + 1);
  setFlag(f => !f);
  // These will be batched together in React 18
}

2. Memoization Techniques

 

React.memo:

Use React.memo to prevent unnecessary re-renders of functional components. It memoizes the component and only re-renders it if its props change.

Example:

const MyComponent = React.memo((props) => {
  // component code
});

useMemo:

Use useMemo to memoize expensive calculations or derived data so that they are only recalculated when their dependencies change.

Example:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useCallback:

Use useCallback to memoize functions that are passed as props to child components, preventing those children from re-rendering unnecessarily.

Example:

const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

3. Code Splitting

 

Dynamic Import with React.lazy:

Split your code into smaller chunks by using React.lazy and Suspense to load components only when needed. This reduces the initial load time.

Example:

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

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <MyComponent />
    </Suspense>
  );
}

4. Avoid Unnecessary Re-renders

 

Keying Components Properly:

Ensure that you use stable and unique keys in lists. Improper keys can lead to unnecessary re-renders or component remounting.

Avoid Inline Functions and Objects:

Passing inline functions or objects as props can cause child components to re-render unnecessarily because each render creates a new reference.

Example:

// Instead of this
<ChildComponent onClick={() => doSomething()} />
// Do this
const handleClick = useCallback(() => doSomething(), []);
<ChildComponent onClick={handleClick} />

5. Use React DevTools Profiler

 

React DevTools Profiler helps identify performance bottlenecks in your application by showing you which components are rendering frequently and taking the most time.

 

6. Avoid Mounting Large Components

 

Conditional Rendering:

Delay rendering components that are not needed immediately. Use conditional rendering or Suspense to load these components only when required.

Virtualize Long Lists:

Use libraries like react-window or react-virtualized to render only the visible part of long lists, improving performance by reducing DOM nodes.

 

7. Optimize Images and Media

 

Lazy Load Images:

Use loading="lazy" for images to load them only when they enter the viewport.

Responsive Images:

Serve appropriately sized images based on the user’s device to reduce download sizes.

 

8. Debounce or Throttle Expensive Operations

 

Debouncing and Throttling:

Debounce or throttle frequent events like window resizing, scrolling, or typing to reduce the number of state updates and re-renders.

Example using useCallback and lodash:

const handleResize = useCallback(
  _.debounce(() => {
    // handle resize
  }, 300),
  []
);

9. Use PureComponent or shouldComponentUpdate in Class Components

 

For class components, use React.PureComponent or manually implement shouldComponentUpdate to prevent unnecessary re-renders based on shallow prop comparisons.

 

10. Optimize Third-Party Libraries

 

Tree Shaking:

Ensure that your build process removes unused code from third-party libraries by enabling tree shaking, especially when using large UI frameworks or utility libraries.

Use Lightweight Alternatives:

Consider using lighter alternatives to heavy libraries where possible.

 

11. Server-Side Rendering (SSR) and Static Site Generation (SSG)

Optimize SSR and SSG:

Utilize SSR and SSG to pre-render pages on the server, improving load times and SEO. React 18’s useId hook can help manage unique IDs in SSR without causing hydration mismatches.

12. Optimize State Management

Avoid Overuse of Global State:

Only use global state (like Redux or Context API) when necessary. Overusing global state can cause performance issues as it may trigger re-renders across many components.

Local State in Components:

Keep state local to components whenever possible to minimize the re-render scope.

 

Conclusion

 

By applying these techniques and taking advantage of React 18’s new features, you can significantly improve the performance of your React application. Focus on preventing unnecessary re-renders, optimizing data fetching, and making sure your application only does what’s necessary to render efficiently.

Also reading comparing react 18 vs react 17

Huy Le
Huy Le
[email protected]

Full-stack developer passionate about React and react native, firebase

Tags
reactjs
reactjs performance
web performance
Social Share
Loading...