JAVASCRIPT

Optimize Performance with `useCallback` Hook for Stable Functions

Enhance React application performance by using the `useCallback` hook to memoize event handlers and callback functions. Prevent needless re-renders in child components by providing stable function references across renders, crucial for `React.memo`.

import React, { useState, useCallback } from 'react';

// Child Component
const Button = React.memo(({ onClick, label }) => {
  console.log(`Rendering ${label} Button`);
  return (
    <button onClick={onClick} style={{ padding: '10px 15px', margin: '5px', borderRadius: '4px', border: '1px solid #007bff', background: '#e7f3ff', cursor: 'pointer' }}>
      {label}
    </button>
  );
});

// Parent Component
function ParentComponent() {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);
  const [randomState, setRandomState] = useState(0);

  // This function is re-created on every render of ParentComponent
  // If passed to a memoized child, it will cause re-renders.
  const handleClickBad = () => {
    setCount1(prev => prev + 1);
  };

  // This function is memoized and only re-created if its dependencies change.
  // If passed to a memoized child, it prevents unnecessary re-renders.
  const handleClickGood = useCallback(() => {
    setCount2(prev => prev + 1);
  }, []); // Empty dependency array means it's created once

  console.log('Rendering ParentComponent');

  return (
    <div style={{ padding: '20px', border: '1px solid #ccc', borderRadius: '5px' }}>
      <h1>useCallback for Performance Optimization</h1>
      <p>
        Observe the console logs to see which buttons cause re-renders of the child components.
        'Button' components are memoized with `React.memo`.
      </p>

      <h2 style={{ marginTop: '20px' }}>Counter 1: {count1}</h2>
      <Button onClick={handleClickBad} label="Increment Count 1 (Bad Callback)" />
      <p style={{ fontSize: '0.9em', color: '#666' }}>
        <code>handleClickBad</code> is re-created on every render of ParentComponent.
        Even though the child `Button` is memoized, passing a new function reference
        will cause it to re-render when ParentComponent updates.
      </p>

      <hr style={{ margin: '30px 0' }}/>

      <h2 style={{ marginTop: '20px' }}>Counter 2: {count2}</h2>
      <Button onClick={handleClickGood} label="Increment Count 2 (Good Callback with useCallback)" />
      <p style={{ fontSize: '0.9em', color: '#666' }}>
        <code>handleClickGood</code> is wrapped in `useCallback` with an empty dependency array.
        It retains the same function reference across renders, preventing `Button` from
        re-rendering unnecessarily when `ParentComponent` updates other states.
      </p>

      <hr style={{ margin: '30px 0' }}/>

      <h2 style={{ marginTop: '20px' }}>Random State: {randomState}</h2>
      <button onClick={() => setRandomState(Math.random())} style={{ padding: '10px 15px', margin: '5px', borderRadius: '4px', background: '#f8d7da', border: '1px solid #dc3545', cursor: 'pointer' }}>
        Update Random State (Causes Parent Re-render)
      </button>
      <p style={{ fontSize: '0.9em', color: '#666' }}>
        Clicking this button will re-render the ParentComponent. Notice how only "Increment Count 1 (Bad Callback)"
        causes its child `Button` to re-render, while "Increment Count 2 (Good Callback)" does not.
      </p>
    </div>
  );
}

export default ParentComponent;
How it works: The `useCallback` hook in React is used for memoizing functions. It returns a memoized version of the callback function that only changes if one of the dependencies has changed. This is particularly useful when passing callbacks to optimized child components (like those wrapped in `React.memo`). Without `useCallback`, a new function instance would be created on every render of the parent component, causing memoized children to re-render unnecessarily because their props (the callback function) have changed by reference. By ensuring a stable function reference, `useCallback` helps to prevent these unwanted re-renders, leading to improved application performance and a more efficient rendering cycle.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs