useCallBack in React

The useCallback hook in React is a performance optimization tool that returns a memoized version of the callback function that only changes if one of the dependencies has changed. This hook is particularly useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders. In this chapter, we'll dive into the useCallback hook, from basic to advanced usage, with practical examples to demonstrate its benefits.

Understanding useCallback

The useCallback hook is used to memoize functions, preventing them from being recreated on every render unless their dependencies change. This can help optimize performance in React applications by avoiding unnecessary renders.

Syntax:

				
					const memoizedCallback = useCallback(() => {
  // Function logic
}, [dependency1, dependency2]);

				
			
  • The first argument is the function to be memoized.
  • The second argument is an array of dependencies. The memoized function is recalculated only when one of these dependencies changes.

Basic Usage

Let’s start with a simple example to understand the basic usage of useCallback.

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

function App() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

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

  return (
    <div style={{ padding: '20px' }}>
      <h1>useCallback Example</h1>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Type something"
      />
    </div>
  );
}

export default App;

				
			

Explanation:

  • State Variables: We have two state variables, count and text.
  • Callback Function: The increment function is memoized using useCallback and will only be recreated if its dependencies change.
  • Rendering: The memoized increment function is used to increment the count, while the text input updates independently.

Output:

When you run this application, you’ll see the count and a text input. The count can be incremented using the button, and the text input updates without causing the increment function to be recreated.

Practical Examples

Optimizing Re-renders with useCallback

In this example, we will demonstrate how useCallback can prevent unnecessary re-renders of child components by ensuring that the reference to the callback function remains stable.

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

const ChildComponent = memo(({ onClick, count }) => {
  console.log('Rendering ChildComponent');
  return (
    <div>
      <p>Count in Child: {count}</p>
      <button onClick={onClick}>Increment Count in Child</button>
    </div>
  );
});

function App() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

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

  return (
    <div style={{ padding: '20px' }}>
      <h1>useCallback Example: Preventing Re-renders</h1>
      <ChildComponent onClick={increment} count={count} />
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Type something"
      />
    </div>
  );
}

export default App;

				
			

Explanation:

  • Child Component: The ChildComponent is wrapped with memo to prevent unnecessary re-renders.
  • Callback Function: The increment function is memoized using useCallback to ensure it retains the same reference across renders unless its dependencies change.
  • Rendering: The ChildComponent receives the memoized increment function and the count. It only re-renders when the count changes, not when the text input updates.

Output:

When you run this application, the console will log “Rendering ChildComponent” only when the count changes, demonstrating that the child component is not re-rendering unnecessarily when the text input updates.

Handling Complex Callbacks

In this example, we will use useCallback to handle a more complex callback function that depends on multiple state variables.

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

function App() {
  const [count, setCount] = useState(0);
  const [factor, setFactor] = useState(2);

  const multiply = useCallback(() => {
    return count * factor;
  }, [count, factor]);

  return (
    <div style={{ padding: '20px' }}>
      <h1>useCallback Example: Complex Callbacks</h1>
      <p>Count: {count}</p>
      <p>Factor: {factor}</p>
      <p>Result: {multiply()}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <button onClick={() => setFactor(factor + 1)}>Increment Factor</button>
    </div>
  );
}

export default App;

				
			

Explanation:

  • State Variables: We have two state variables, count and factor.
  • Callback Function: The multiply function is memoized using useCallback and recalculated only when count or factor changes.
  • Rendering: The result of the multiply function is displayed alongside the count and factor values.

Output:

When you run this application, you’ll see the count, factor, and the result of multiplying them. The multiply function will be recalculated only when either the count or factor changes.

Advanced Usage

Passing Functions to Custom Hooks

useCallback is also useful when passing functions to custom hooks, ensuring that the functions have stable references and do not cause unnecessary re-renders or effects.

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

function useCustomHook(callback) {
  useEffect(() => {
    callback();
  }, [callback]);
}

function App() {
  const [count, setCount] = useState(0);

  const customFunction = useCallback(() => {
    console.log('Custom function called with count:', count);
  }, [count]);

  useCustomHook(customFunction);

  return (
    <div style={{ padding: '20px' }}>
      <h1>useCallback Example: Custom Hook</h1>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
    </div>
  );
}

export default App;

				
			

Explanation:

  • Custom Hook: useCustomHook accepts a callback function and invokes it within an useEffect hook.
  • Callback Function: The customFunction is memoized using useCallback and recalculated only when count changes.
  • Rendering: The custom function is called within the custom hook whenever the count changes.

Output:

When you run this application, you’ll see the count and a button to increment it. The console will log “Custom function called with count:” followed by the current count value whenever the count changes.

Practical Example 1: Memoizing an Event Handler to Prevent Unnecessary Re-renders

In this example, we will use useCallback to memoize an event handler function passed to a child component. This will prevent the child component from re-rendering unnecessarily.

				
					// ChildComponent.js

import React, { memo } from 'react';

const ChildComponent = memo(({ onClick }) => {
  console.log('Rendering ChildComponent');
  return (
    <div>
      <button onClick={onClick}>Click me</button>
    </div>
  );
});

export default ChildComponent;

				
			
				
					// App.js

import React, { useState, useCallback } from 'react';
import ChildComponent from './ChildComponent';

function App() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

  const handleClick = useCallback(() => {
    console.log('Button clicked');
  }, []);

  return (
    <div style={{ padding: '20px' }}>
      <h1>useCallback Example: Memoizing Event Handler</h1>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Type something"
      />
      <ChildComponent onClick={handleClick} />
    </div>
  );
}

export default App;

				
			

Explanation:

  • Child Component: ChildComponent is wrapped with memo to prevent unnecessary re-renders.
  • Callback Function: handleClick is memoized using useCallback, ensuring it has a stable reference.
  • Rendering: The child component only re-renders if the onClick prop changes, which doesn’t happen due to the stable reference of handleClick.

Output:

When you run this application, the console will log “Rendering ChildComponent” only when the app first renders. Clicking the “Increment Count” button or typing in the input field will not cause the child component to re-render, demonstrating the effectiveness of useCallback in preventing unnecessary re-renders.

Practical Example 2: Memoizing a Function with Dependencies

In this example, we will use useCallback to memoize a function that has dependencies. This is useful when the function’s logic depends on state variables or props.

				
					// ChildComponent.js

import React, { memo } from 'react';

const ChildComponent = memo(({ calculate }) => {
  console.log('Rendering ChildComponent');
  const result = calculate();
  return (
    <div>
      <p>Calculation Result: {result}</p>
    </div>
  );
});

export default ChildComponent;

				
			
				
					// App.js

import React, { useState, useCallback } from 'react';
import ChildComponent from './ChildComponent';

function App() {
  const [count, setCount] = useState(0);
  const [factor, setFactor] = useState(2);

  const calculate = useCallback(() => {
    return count * factor;
  }, [count, factor]);

  return (
    <div style={{ padding: '20px' }}>
      <h1>useCallback Example: Function with Dependencies</h1>
      <p>Count: {count}</p>
      <p>Factor: {factor}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
      <button onClick={() => setFactor(factor + 1)}>Increment Factor</button>
      <ChildComponent calculate={calculate} />
    </div>
  );
}

export default App;

				
			

Explanation:

  • State Variables: We have two state variables, count and factor.
  • Callback Function: The calculate function is memoized using useCallback and recalculated only when count or factor changes.
  • Child Component: ChildComponent is wrapped with memo to prevent unnecessary re-renders and receives the calculate function as a prop.
  • Rendering: The child component re-renders only when the memoized calculate function changes, which happens when either count or factor changes.

Output:

When you run this application, the console will log “Rendering ChildComponent” whenever the calculate function is recalculated due to changes in count or factor. The result of the calculation is displayed in the child component. This demonstrates how useCallback can be used effectively to memoize functions with dependencies, ensuring optimal performance.

The useCallback hook in React is essential for optimizing performance by memoizing callback functions and preventing unnecessary re-renders. By understanding and effectively using useCallback, you can enhance the efficiency of your React applications, especially in scenarios involving complex callbacks and optimized child components. These examples demonstrate the power and flexibility of useCallback in real-world scenarios, helping you understand how to apply it effectively in your projects. Happy coding !❤️

Table of Contents

Contact here

Copyright © 2025 Diginode

Made with ❤️ in India