Boost React Performance: Master Functional Components, Hooks, and Memoization

This article explains how functional components, React hooks, and memoization techniques such as React.memo, useCallback, and useMemo can be combined to reduce unnecessary re‑renders, simplify side‑effect management, and improve overall UI performance in modern React applications.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Boost React Performance: Master Functional Components, Hooks, and Memoization

1. Introduction

Functional components provide a concise, data‑driven way to build UI. By splitting a React component into three parts—data, computation, and rendering—we can identify several performance‑optimization directions.

Data: use caching to reduce the number of rerender calls.

Computation: precisely determine when and how much to update, reducing computation load.

Render: fine‑grained rendering lowers component complexity.

Today we mainly share data‑layer performance‑optimization techniques.

1.1 What can Hooks do that class components cannot?

While learning the React hook api, I discovered that hooks are more abstract and flexible than class lifecycle methods. The React FAQ asks a fascinating question— What can Hooks do that class components cannot?

Recently I found an answer while developing a feature that required passing a parameter via URL or cache to a newly opened tab. When the tab page loads, it reads the parameter and uses it to request additional data for rendering.

Initially I implemented the feature with a class component, but the code was hard to understand and maintain. Refactoring to a functional component made the code much cleaner.

1.2 A bad example (getDerivedStateFromProps + componentDidUpdate)

First I tried to use getDerivedStateFromProps and componentDidUpdate to compare props and control requests and updates.

We have a parameter called productId (also known as 商品id) that comes from the URL or cache. When the parent component receives an updated productId, it passes it to the child component via props.

function Parent() {
  /**
   * When productId updates, pass it to Child
   */
  return <Child productId={productId} />
}

When the parent changes the productId prop, the child uses getDerivedStateFromProps to detect the change, updates its state, and triggers a re‑render.

static getDerivedStateFromProps(nextProps, prevState) {
  const { productId } = nextProps;
  if (productId !== prevState.productId) {
    return { productId };
  }
  return null;
}

After the state update, componentDidUpdate sends another request to fetch the product details.

componentDidUpdate(prevProps, prevState) {
  // When state changes, request new data
  if (prevState.productId !== this.state.productId) {
    this.getProductDetailRequest();
  }
}

getProductDetailRequest = async () => {
  const { productId } = this.state;
  const { result } = await getProductDetail({ f_id: +productId });
  this.setState({ productDetail: result });
};

This implementation has several drawbacks:

Calling setState inside componentDidUpdate without proper guards can cause infinite loops.

State comparison is scattered across two lifecycle methods, making maintenance harder.

The demo code is overly complex for a simple use case.

1.3 Another bad example (componentWillReceiveProps)

Because getDerivedStateFromProps is static and cannot access this, side‑effects such as async requests cannot be placed there. Therefore we might fall back to componentWillReceiveProps to trigger a request when new props arrive.

componentWillReceiveProps(props) {
  const { productId } = props;
  if (`${productId}` === 'null') return; // no request
  if (productId !== this.state.productId) {
    this.getProductDetailRequest(productId);
  }
}

This approach works only for React versions prior to 16.4.

1.4 Managing side‑effects in class components

Two common solutions exist, each with drawbacks:

componentWillReceiveProps was marked unsafe in React 16.4 because it is easy to misuse and introduce side‑effects.

Combining getDerivedStateFromProps with componentDidUpdate adds complexity without real benefit.

Since React 16.8, functional components with hooks provide a cleaner way to handle side‑effects.

2. Functional Component Performance Optimizations

2.1 Pure Components

Pure components stem from the concept of pure functions: the output depends only on the input and is deterministic. In React, a component that renders the same UI for identical props and state can be treated as pure.

2.1.1 Shallow Comparison

Shallow comparison checks primitive values for equality and reference values for identical memory addresses. It runs before the diff algorithm, preventing unnecessary re‑renders with less overhead.

2.1.2 Pure Component APIs

Pure components perform a shallow comparison of props and state, similar to implementing shouldComponentUpdate manually.

Class components can extend PureComponent; functional components can be wrapped with React.memo.

import React, { PureComponent } from 'react';

class App extends PureComponent {}
export default App;

If deep changes occur in referenced data, a pure component will not update, so custom shouldComponentUpdate may be needed.

2.1.3 React.memo

React.memo

is a higher‑order component that memoizes the result of a functional component based on shallow prop comparison.

function MyComponent(props) {
  /* render using props */
}
function areEqual(prevProps, nextProps) {
  // custom comparison logic
}
export default React.memo(MyComponent, areEqual);

When a component uses useState, useReducer, or useContext, changes in state or context will still cause a re‑render even if props are unchanged.

2.1.4 Summary

Converting class components to functional components and applying React.memo is the most convenient way to create pure components.

2.2 useCallback

Passing callback functions via props can cause child components to re‑render on every parent render. Wrapping the callback with useCallback memoizes it, updating only when its dependencies change.

const Parent = () => {
  const [title, setTitle] = useState('Title');
  const callback = () => {
    setTitle('Changed Title');
  };
  const memoizedCallback = useCallback(callback, []);
  return (<>
    <h1>{title}</h1>
    <Child onClick={memoizedCallback} />
  </>);
};

const Child = ({ onClick }) => (
  <button onClick={onClick}>change title</button>
);
useCallback(fn, deps)

is equivalent to useMemo(() => fn, deps).

2.3 useMemo

useMemo

caches the result of an expensive calculation and recomputes only when its dependencies change.

function computeExpensiveValue() {
  // long calculation
  return xxx;
}
const memoizedValue = useMemo(computeExpensiveValue, [a, b]);

Example with factorial calculation:

export function CalculateFactorial() {
  const [number, setNumber] = useState(1);
  const [inc, setInc] = useState(0);

  const factorial = useMemo(() => factorialOf(number), [number]);

  const onChange = event => setNumber(Number(event.target.value));
  const onClick = () => setInc(i => i + 1);

  return (
    <div>
      Factorial of <input type="number" value={number} onChange={onChange} /> is {factorial}
      <button onClick={onClick}>Re‑render</button>
    </div>
  );
}

function factorialOf(n) {
  console.log('factorialOf(n) called!');
  return n <= 0 ? 1 : n * factorialOf(n - 1);
}

When inc changes, the component re‑renders, but because number is unchanged, the memoized factorial value is reused.

3. Conclusion

1. Combining functional components with the hook API offers a cleaner way to manage side‑effects, making it the preferred approach for the scenarios discussed in the introduction.

2. In plain terms, React.memo caches the virtual DOM, useCallback caches functions, and useMemo caches values.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

performance optimizationReactmemoizationfunctional components
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.