Master React Optimization Technique
Tags: react, nextjs, javascript, webdev
Published: 2024-05-18 05:35:24

In today's fast-paced web landscape, a React application's performance is paramount. A sluggish app can lead to frustrated users and lost conversions. Fortunately, React provides a plethora of built-in features and libraries to streamline your application and deliver a seamless experience. Here are 7 optimization techniques that will elevate your React app to production-grade quality

## 01. Component Memoization

To achieve memoization we can employee **React.memo** which is a higher-order component (HOC) that prevents unnecessary re-renders of functional components by **memoizing** the component output based on its props. It re-renders only if the props or the component's state change.

```javascript
import React from 'react';

const MyComponent = React.memo(({ value }) => {
  console.log('Rendered');
  return <div>{value}</div>;
});

export default MyComponent;

```

#### Using useCallback and useMemo for Stable Reference

Similar to useMemo but for functions, useCallback prevents unnecessary function recreation when its dependencies haven't changed. This is particularly helpful for callback functions passed as props.

```javascript
import React, { useCallback, useMemo, useState } from 'react';

const MyComponent = ({ items }) => {
  const [count, setCount] = useState(0);

  const calculateTotal = useMemo(() => {
    return items.reduce((total, item) => total + item.price, 0);
  }, [items]);

  const increment = useCallback(() => {
    setCount((prevCount) => prevCount + 1);
  }, []);

  return (
    <div>
      <p>Total: {calculateTotal}</p>
      <button onClick={increment}>Increment: {count}</button>
    </div>
  );
};

export default MyComponent;

```

## 02. Code Splitting Technique

Lazy loading allows you to load components or modules only when they're needed. This reduces the initial bundle size and improves load times, especially for complex applications. React's built-in React.lazy and Suspense components facilitate this process.

```javascript
import React, { lazy, Suspense } from 'react';

const MyLazyComponent = lazy(() => import('./MyLazyComponent'));

function MyComponent() {
  return (
    <div>
      <button onClick={() => import('./MyLazyComponent')}>Load Lazy Component</button>
      <Suspense fallback={<div>Loading...</div>}>
        <MyLazyComponent />
      </Suspense>
    </div>
  );
}

```

## 03. Efficient Event Handling

For frequently triggered events, consider **Throttling** or **debouncing** techniques to reduce the number of function calls. Throttling ensures the function executes at most once within a specified time interval, while debouncing only executes it after a period of inactivity.

#### Debouncing
```javascript
import React, { useState } from 'react';
import { debounce } from 'lodash';

const Search = () => {
  const [query, setQuery] = useState('');

  const handleSearch = debounce((event) => {
    setQuery(event.target.value);
    // Perform search operation
  }, 300);

  return <input type="text" onChange={handleSearch} />;
};

export default Search;
```

#### Throttling
```javascript
import React, { useEffect, useRef } from 'react';

const AnimationComponent = () => {
  const ref = useRef();

  useEffect(() => {
    const animate = () => {
      // Update animation frame
      requestAnimationFrame(animate);
    };
    requestAnimationFrame(animate);
  }, []);

  return <div ref={ref}>Animating...</div>;
};

export default AnimationComponent;
```

## 04. Rendering Long Lists (Virtualized Lists)

When dealing with extensive lists, list virtualization becomes crucial. It renders only the visible items on the screen, significantly improving performance and reducing DOM manipulation. Popular libraries like react-window and react-virtualized offer efficient solutions

```javascript
import React from 'react';
import { FixedSizeList as VirtualizedList } from 'react-window';

const ListItem = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);

const MyList = ({ items }) => (
  <VirtualizedList
    height={150}
    itemCount={items.length}
    itemSize={35}
    width={300}
  >
    {ListItem}
  </VirtualizedList>
);

export default MyList;
```

## 05. Track Performance Bottlenecks

Profiling is the cornerstone of optimization. Use React DevTools Profiler to identify performance bottlenecks in your components. It pinpoints areas consuming excessive rendering time, guiding your optimization efforts.

```javascript
import React, { Profiler } from 'react';

const onRenderCallback = (
  id, // the "id" prop of the Profiler tree that has just committed
  phase, // either "mount" (if the tree just mounted) or "update" (if it re-rendered)
  actualDuration, // time spent rendering the committed update
  baseDuration, // estimated time to render the entire subtree without memoization
  startTime, // when React began rendering this update
  commitTime, // when React committed this update
  interactions // the Set of interactions belonging to this update
) => {
  console.log('Render time:', actualDuration);
};

const App = () => (
  <Profiler id="App" onRender={onRenderCallback}>
    <MyComponent />
  </Profiler>
);

export default App;
```

## 06. Optimize Context Usage

Using useContextSelector from the use-context-selector library can help in avoiding unnecessary re-renders when using context.

```javascript
import React from 'react';
import { useContextSelector } from 'use-context-selector';

const CountContext = React.createContext();

const Display = () => {
  const count = useContextSelector(CountContext, state => state.count);
  return <div>{count}</div>;
};

const App = () => {
  const [count, setCount] = React.useState(0);
  const contextValue = { count, setCount };

  return (
    <CountContext.Provider value={contextValue}>
      <Display />
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </CountContext.Provider>
  );
};

export default App;
```

## 07. Usage of Fragment Components
Avoid unnecessary DOM nodes by wrapping JSX elements with React.Fragment. This prevents creating extra <div> elements for layout purposes.

```javascript
import React, { Fragment } from 'react';

function MyComponent() {
  return (
    <Fragment>
      <p>Item 1</p>
      <p>Item 2</p>
    </Fragment>
  );
}

// OR

function MyComponent() {
  return (
    <>
      <p>Item 1</p>
      <p>Item 2</p>
    </>
  );
}
```