Popular Searches
Popular Course Categories
Popular Courses

useCallback Hook

useCallback Hook

Advanced React Hooks

React useCallback Hook – Complete Notes

The useCallback Hook is a built-in React Hook used to cache a function definition between re-renders. It helps optimize application performance by maintaining the same function reference until its dependencies change. It is especially useful when passing callback functions to child components that use memo.

Course Link: JustAcademy React JS Training Course

Course Demo: Register for React JS Course Demo


1. What Is the useCallback Hook?

useCallback allows React to reuse a function reference between renders when the specified dependencies have not changed. It is primarily used as a performance optimization when function identity matters.

In JavaScript, defining a function inside a component generally creates a new function whenever the component renders. This can cause a memoized child component to re-render unnecessarily if that function is passed as a prop. The useCallback Hook can help prevent this when its dependencies remain unchanged.

Example

import { useCallback } from 'react';
function App() {
  const handleClick = useCallback(() => {
    console.log('Button clicked!');
  }, []);
  return <button onClick={handleClick}>Click Me</button>;
}
export default App;

Explanation:

  • useCallback caches the function definition.
  • The empty dependency array means the function can be reused across renders while the cache remains valid.
  • The function runs when the button is clicked, not when useCallback is declared.

2. Syntax of useCallback

const cachedFunction = useCallback(functionDefinition, dependencies);

Parameters:

Parameter Description
functionDefinition The function you want React to cache.
dependencies An array of reactive values used inside the function, such as props, state, and variables declared in the component.

Return value: React returns the function passed to useCallback on the initial render. On later renders, React returns the cached function if the dependencies have not changed; otherwise, it returns the new function.


3. Why Do We Use useCallback?

  • To preserve a function reference between renders when dependencies remain unchanged.
  • To help memoized child components avoid unnecessary re-renders.
  • To maintain stable callback dependencies for other Hooks when needed.
  • To optimize custom Hooks that return functions.
  • To reduce avoidable rendering work in performance-sensitive components.

Important: useCallback does not automatically make every component faster. Use it when profiling or your component structure shows that a stable function reference is beneficial.


4. Example Without useCallback

import { useState, memo } from 'react';
const Child = memo(function Child({ onClick }) {
  console.log('Child rendered');
  return <button onClick={onClick}>Child Button</button>;
});
function App() {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    console.log('Child button clicked');
  };
  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <Child onClick={handleClick} />
    </div>
  );
}
export default App;

Explanation: Every time App renders, it creates a new handleClick function. Because the function reference changes, the memoized Child receives a changed prop and can render again.


5. Example With useCallback

import { useState, useCallback, memo } from 'react';
const Child = memo(function Child({ onClick }) {
  console.log('Child rendered');
  return <button onClick={onClick}>Child Button</button>;
});
function App() {
  const [count, setCount] = useState(0);
  const handleClick = useCallback(() => {
    console.log('Child button clicked');
  }, []);
  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <Child onClick={handleClick} />
    </div>
  );
}
export default App;

Explanation: The handleClick reference remains stable while its dependencies stay unchanged. Because Child is wrapped in memo and its other props remain unchanged, React can skip rendering it when the counter changes.

Remember: useCallback alone does not prevent a child component from rendering. It is most useful here because it works together with memo.


6. useCallback with Dependencies

The dependency array tells React which reactive values the function uses. React compares each dependency with its previous value using Object.is.

Example

import { useState, useCallback } from 'react';
function App() {
  const [name, setName] = useState('Rahul');
  const greet = useCallback(() => {
    console.log('Hello ' + name);
  }, [name]);
  return (
    <div>
      <h2>Name: {name}</h2>
      <button onClick={() => setName('Amit')}>Change Name</button>
      <button onClick={greet}>Greet</button>
    </div>
  );
}
export default App;

Explanation: The greet function uses the name state, so name must be included in the dependency array. When the name changes, React creates and returns an updated function that uses the new name.

Dependency Array Types

Dependency Behavior Example
Empty array Reuses the cached function while the cache remains valid. useCallback(fn, [])
With dependencies Returns the cached function until a dependency changes. useCallback(fn, [name])
Array omitted Returns a new function on every render, so caching is not achieved. useCallback(fn)

7. useCallback with useState

You can use useCallback with useState to create stable event handlers that update state.

Example: Counter

import { useState, useCallback } from 'react';
function Counter() {
  const [count, setCount] = useState(0);
  const increment = useCallback(() => {
    setCount(previousCount => previousCount + 1);
  }, []);
  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={increment}>Increment</button>
    </div>
  );
}
export default Counter;

Explanation: The updater function receives the previous state and calculates the next value. Since the callback does not read count directly, it does not need count in its dependency array.


8. useCallback with Event Handlers

Event handlers perform actions such as submitting forms, clicking buttons, deleting items, or updating lists. When a handler is passed to a memoized child component, useCallback can help keep its reference stable.

Example: Button Click

import { useCallback } from 'react';
function App() {
  const handleClick = useCallback(() => {
    alert('Welcome to React JS!');
  }, []);
  return <button onClick={handleClick}>Click Here</button>;
}
export default App;

Explanation: The callback displays an alert when the button is clicked. Its function reference can be reused across renders while its dependencies remain unchanged.


9. useCallback with Forms

useCallback can be used to maintain a stable form submission handler, especially when the handler is passed to a memoized child component.

Example: Form Submission

import { useState, useCallback } from 'react';
function ContactForm() {
  const [email, setEmail] = useState('');
  const handleSubmit = useCallback((event) => {
    event.preventDefault();
    alert('Submitted email: ' + email);
  }, [email]);
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={event => setEmail(event.target.value)}
        placeholder="Enter email"
        required
      />
      <button type="submit">Submit</button>
    </form>
  );
}
export default ContactForm;

Explanation: The handler prevents the browser's default form submission and displays the email entered by the user. Because it reads email, the dependency array includes email.


10. useCallback vs useMemo

Both Hooks are used for memoization, but they cache different things.

Feature useCallback useMemo
Purpose Caches a function definition. Caches the result of a calculation.
Returns A function. A calculated value.
Common use Stable callbacks passed to memoized components. Expensive calculations or stable object/value references.
Example useCallback(() => doWork(), []) useMemo(() => calculate(), [])

Example

const handleClick = useCallback(() => {
  console.log('Clicked');
}, []);
const total = useMemo(() => {
  return price * quantity;
}, [price, quantity]);

In simple words: useCallback caches the function; useMemo caches the result returned by a calculation.


11. useCallback vs useEffect

Feature useCallback useEffect
Purpose Caches a function reference. Runs side-effect logic after rendering when its dependencies require it.
Typical use Stable event handlers or function props. Fetching data, subscriptions, timers, or synchronizing with external systems.
Execution Does not execute the callback automatically. Executes its effect setup function as specified by its dependencies.

12. useCallback with React.memo

React.memo can skip rendering a component when its props have not changed. If a parent passes a newly created function on every render, that function prop can prevent the optimization. Combining memo with useCallback can help when the callback's dependencies remain unchanged.

import { useState, useCallback, memo } from 'react';
const Child = memo(function Child({ onAction }) {
  console.log('Child rendered');
  return <button onClick={onAction}>Perform Action</button>;
});
function Parent() {
  const [count, setCount] = useState(0);
  const handleAction = useCallback(() => {
    console.log('Action performed');
  }, []);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase Count</button>
      <Child onAction={handleAction} />
    </div>
  );
}
export default Parent;

Result: Clicking Increase Count updates the parent counter. Since the callback reference is stable and the child's other props have not changed, React can skip rendering the memoized child.


13. Advantages of useCallback

  • Helps preserve function references between renders.
  • Can support performance optimizations with React.memo.
  • Can help keep function dependencies stable for other Hooks.
  • Can make callbacks returned from custom Hooks more stable for consumers.
  • Can reduce unnecessary work in components with expensive rendering.

14. Limitations of useCallback

  • It does not automatically prevent a component from rendering.
  • It does not stop the function from being defined during rendering; React may reuse the cached function instead.
  • Incorrect dependencies can cause a callback to use outdated values.
  • Using it everywhere can make code harder to read without improving performance.
  • React may discard its cache in specific situations, so it should not be used as a substitute for state or a persistent storage mechanism.

15. Common Mistakes

Mistake Correct Approach
Forgetting the dependency array. Provide the dependency array as the second argument.
Leaving out a value used inside the callback. Include all required reactive dependencies.
Using useCallback for every function. Use it when stable function identity provides a real benefit.
Expecting it to stop every re-render. Use it with appropriate component memoization and stable props.
Calling it inside a loop or condition. Call Hooks at the top level of a component or custom Hook.
Using an empty array when the callback reads changing state. Include the state dependency or use a functional state updater where appropriate.

16. Rules of useCallback

  • Call useCallback only at the top level of a React function component or custom Hook.
  • Do not call it inside loops, conditions, or nested functions.
  • Include all reactive values used inside the callback in the dependency array.
  • Keep the dependency array's size and order constant.
  • Use it as a performance optimization, not as a requirement for correct program behavior.

17. Practical Example: Todo List

This example uses useCallback to create a stable function for adding tasks. A functional state update avoids needing the current task list in the dependency array.

import { useState, useCallback } from 'react';
function TodoApp() {
  const [todos, setTodos] = useState([]);
  const [task, setTask] = useState('');
  const addTodo = useCallback(() => {
    const trimmedTask = task.trim();
    if (!trimmedTask) return;
    setTodos(previousTodos => [
      ...previousTodos,
      { id: Date.now(), text: trimmedTask }
    ]);
    setTask('');
  }, [task]);
  return (
    <div>
      <h2>Todo List</h2>
      <input
        value={task}
        onChange={event => setTask(event.target.value)}
        placeholder="Enter a task"
      />
      <button onClick={addTodo}>Add Task</button>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>{todo.text}</li>
        ))}
      </ul>
    </div>
  );
}
export default TodoApp;

How it works:

  1. The user enters a task in the input field.
  2. The Add Task button calls addTodo.
  3. The callback adds a new task using the functional state updater.
  4. The input is cleared and React renders the updated list.
  5. The callback is recreated when task changes because the function uses that value.

18. Interview Questions and Answers

Q1. What is useCallback in React?

Answer: useCallback is a React Hook that caches a function definition between renders while its dependencies remain unchanged.

Q2. Why is useCallback used?

Answer: It is used to preserve function identity when that matters, such as when passing callbacks to memoized child components or using a function as a Hook dependency.

Q3. What is the syntax of useCallback?

Answer: const cachedFn = useCallback(fn, dependencies);

Q4. What is the difference between useCallback and useMemo?

Answer: useCallback caches a function definition, whereas useMemo caches the result of a calculation.

Q5. Does useCallback prevent re-rendering?

Answer: No. It preserves a function reference when dependencies remain unchanged. In combination with React.memo, it can help a child component skip unnecessary re-renders.

Q6. What happens when a dependency changes?

Answer: React returns the function definition from the current render and caches it for future renders.

Q7. Can useCallback be used without dependencies?

Answer: You can omit the dependency array, but React then returns a new function on every render, so caching is not achieved.

Q8. Can useCallback be used inside a loop?

Answer: No. Hooks must be called at the top level. If each list item needs its own callback, extract a separate component for each item.

Q9. Is useCallback always necessary?

Answer: No. Use it when stable function identity provides a measurable or practical benefit. Otherwise, a normal function is often simpler.

Q10. How can dependencies be reduced safely?

Answer: When a callback only needs the previous state to calculate the next state, use a functional updater such as setCount(previous => previous + 1). Do not remove dependencies if the callback still needs their current values.


19. Practice Exercises

  1. Create a counter using useState and a memoized increment callback.
  2. Create a form submission handler using useCallback.
  3. Pass a callback to a child component wrapped in React.memo.
  4. Create a Todo List with add and delete handlers.
  5. Write a callback that uses a state variable and specify the correct dependencies.

20. Best Practices Checklist

  • Understand why a stable function reference is needed before using useCallback.
  • Specify all necessary dependencies.
  • Use functional state updates when appropriate.
  • Combine with React.memo only when it helps avoid unnecessary rendering work.
  • Keep callbacks readable and avoid unnecessary memoization.
  • Use React Developer Tools Profiler to investigate performance problems.
  • Remember that newer React Compiler setups can automatically memoize many values and functions, reducing the need for manual optimization.

21. Official Documentation


22. React JS Training Resources

Learn React JS concepts, practical development skills, and modern application-building techniques through the following resources:

Conclusion

The useCallback Hook helps React developers cache function definitions between renders when their dependencies remain unchanged. It is especially useful with memoized child components and certain Hook dependencies. Correct dependency management is essential, and memoization should be applied thoughtfully rather than to every function.

whatsapp