---
title: "Boosting React Performance: useCallback vs. useMemo Hooks"
published_at: "2023-08-30T11:03:26+00:00"
modified_at: "2026-01-14T08:18:13+00:00"
url: "https://www.syncfusion.com/blogs/post/react-usecallback-vs-usememo-hooks"
excerpt: "This blog explains how React's useCallback and useMemo hooks optimize performance by reducing re-renders and re-computations."
taxonomy_category:
  - "Development"
  - "Essential JS 2"
  - "React"
  - "Web"
taxonomy_post_tag:
  - "API"
  - "development"
  - "React"
  - "React Hooks"
  - "Web"
---

# Boosting React Performance: useCallback vs. useMemo Hooks

[Prashant Yadav](https://www.syncfusion.com/blogs/author/prashant-yadav)

![Boosting React Performance: useCallback vs. useMemo Hooks](https://www.syncfusion.com/blogs/wp-content/uploads/2023/08/Boosting-React-Performance-useCallback-vs.-useMemo-Hooks.png)


[React](https://en.wikipedia.org/wiki/React_(software))
 is a popular JavaScript framework for front-end development that has made developers’ lives easier by introducing component-driven development.

Before React 16, components were usually defined with **Class** and had different lifecycle methods. React 16 promoted the creation of functional components and introduced the following [hooks](https://en.wikipedia.org/wiki/React_(software)#React_Hooks)
:

- **useEffect** for handling the component’s lifecycle.
- **useState** for state management.

These hooks are necessary because re-renders and re-computations are costly operations, so minimizing them improves the application’s performance.

React has recently introduced two hooks, **useCallback** and ** useMemo**. Like the useEffect and useState hooks, these are potent tools that can help developers optimize the performance of their applications by reducing unnecessary re-renders and re-computations.

In this blog, we will delve into these hooks, exploring their functionality and examining how they work. We will also discuss the appropriate use cases for these hooks, providing practical examples to help you understand when and where to use them in your projects. Let’s get started!


## useCallback hook

Syntax

```
const computedFn = useCallback(()=>{
  doSomething(dependencies);
}, [dependencies]);
```

In the previous code example, **doSomething(dependencies);** will be returned and stored in the ** computedFn** variable. This only occurs when the dependencies change, and ** useCallback** will provide the new ** doSomething(dependencies);** value.

This is useful when you want a function to avoid re-rendering whenever the component changes.

For example, we will use the **debounce** function on the input field to get the data only when the user stops writing for a specific amount of time.

```
import React, { useState } from "https://esm.sh/[email protected]";
import ReactDOM from "https://esm.sh/[email protected]";
const App = () => {
  const [text, setText] = useState("");
  const debounce = (func, delay) => {
	let inDebounce;
	return function () {
  	const context = this;
  	const args = arguments;
  	clearTimeout(inDebounce);
  	inDebounce = setTimeout(() => func.apply(context, args), delay);
	};
  };
  const makeApiCall = () => {
	console.log(text, " Making an API call");
  };
  const debouncedApiCall = debounce(makeApiCall, 500);
  const onChangeHandler = (e) => {
	setText(e.target.value);
	debouncedApiCall();
  };
  return (
	<div>
  	<input type="text" onChange={onChangeHandler} value={text} />
	</div>
  );
};
ReactDOM.render(<App />, document.getElementById("root"));
```

Every time the state is updated, the component re-renders, and the debounce function is reattached to the **debouncedApiCall** variable. As a result, the logs are printed every time the state changes, and the ** debounce** needs to be fixed.

To avoid this, we can wrap the **debounce** function inside ** useCallback**, and the program will write the memoized debounce function that will change only if dependencies change.

```
import React, { useState, useCallback } from "https://esm.sh/[email protected]";
import ReactDOM from "https://esm.sh/[email protected]";
const App = () => {
  const [text, setText] = useState("");
  const _debounce = (func, delay) => {
	let inDebounce;
	return function () {
  	 const context = this;
  	 const args = arguments;
  	 clearTimeout(inDebounce);
  	 inDebounce = setTimeout(() => func.apply(context, args), delay);
	};
  };
  const makeApiCall = (e) => {
	console.log(e, "Making an API call");
  };
  const debounce = useCallback(_debounce(makeApiCall, 2000), []);
 
  const onChangeHandler = (e) => {
	setText(e.target.value);
	debounce(e.target.value);
  };
  return (
	<div>
  	<input type="text" onChange={onChangeHandler} value={text} />
	</div>
  );
};
ReactDOM.render(<App />, document.getElementById("root"));
```

We don’t want to re-render the **_debounce** function, so we are not passing any dependencies. This code will print the log only when the user has stopped writing and 2000 ms have passed. Notice in the code that we are passing the value to ** debounce(e.target.value)** because it cannot pull the value of the state.

Even though it seems to be working fine, it is not. If you have **ESLint** enabled, you will notice that we are getting the following linting error for the ** useCallback** hook:

**error React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead react-hooks/exhaustive-deps**

This is because the dependencies of the function that **useCallback** is taking as input are not known, so ** ESLint** wants us to write an inline function.

Also, the **_debounce** function is re-created on every re-render, thus changing its reference and defeating the purpose of caching it.

To fix this, we can create a custom **useDebounce** hook and use it.

```
import { useState, useCallback, useRef } from "react";
const useDebounce = (func, delay) => {
  const inDebounce = useRef();
  const debounce = useCallback(
	function () {
  	const context = this;
  	const args = arguments;
  	clearTimeout(inDebounce.current);
  	inDebounce.current = setTimeout(() => func.apply(context, args), delay);
	},
	[func, delay]
  );
  return debounce;
};
const App = () => {
  const [text, setText] = useState("");
  const makeApiCall = useCallback((e) => {
	console.log(e, "Making an API call");
  }, []);
  const debounce = useDebounce(makeApiCall, 2000);
  const onChangeHandler = (e) => {
	setText(e.target.value);
	debounce(e.target.value);
  };
  return (
	<div>
  	<input type="text" onChange={onChangeHandler} value={text} />
	</div>
  );
};
```


## useMemo hook

Syntax

```
const computedValue = useMemo(()=>{
  doSomething();
}, [dependencies]);
```

**useMemo** works similarly to the ** useCallback**, but rather than returning the function, it returns the computed value from the function, i.e. the function’s output, and only recomputes the function when the dependencies change to provide the new result.

For example, let’s say we have a child component that should render only when the text has a specific value.

```
import React, { useState, useCallback } from "https://esm.sh/[email protected]";
import ReactDOM from "https://esm.sh/[email protected]";
const ChildComponent = ({ text }) => {
  return <p>{text}</p>;
};
const App = () => {
  const [text, setText] = useState("");
  const onChangeHandler = (e) => {
	setText(e.target.value);
  };
  return (
	<div>
  	<input type="text" onChange={onChangeHandler} value={text} />
  	<ChildComponent text={text} />
	</div>
  );
};
ReactDOM.render(<App />, document.getElementById("root"));
```

If the conditional check were placed within the child component, it would trigger a re-render each time the parent’s state updates, regardless of our intention to limit it to certain values.

The **useMemo** hook addresses this. By employing this hook, the child component becomes memoized, ensuring it only re-renders when the conditions specified in its dependencies are satisfied.

In this instance, the child component will exclusively render when the text matches the term “syncfusion.”

```
import React, { useState, useMemo } from "https://esm.sh/[email protected]";
import ReactDOM from "https://esm.sh/[email protected]";
const ChildComponent = ({ text }) => {
  return <p>{text}</p>;
};
const App = () => {
  const [text, setText] = useState("");
  const onChangeHandler = (e) => {
	setText(e.target.value);
  };
 
  const memoizedChildComponent = useMemo(() => <ChildComponent text={text} />, [text === 'syncfusion'])
  return (
	<div>
  	<input type="text" onChange={onChangeHandler} value={text} />
  	{memoizedChildComponent}
	</div>
  );
};
ReactDOM.render(<App />, document.getElementById("root"));
```


## Conclusion

Thanks for reading. This blog delved into the two most potent React hooks, namely **useCallback** and ** useMemo**, explaining their functions and exploring their optimal application to sidestep redundant computations when React components re-render.

I encourage you to explore these exceptional features and share your experience in the comment section below.

Have you experienced Syncfusion? Feel free to download the product setup [here](https://www.syncfusion.com/account/downloads/studio/)
. If you’re new to Syncfusion, enjoy a complimentary [30-day trial](https://www.syncfusion.com/downloads/)
.

Should you need assistance, don’t hesitate to reach out via our [support forums](https://www.syncfusion.com/forums)
, [support portal](https://support.syncfusion.com/)
, or [feedback portal](https://www.syncfusion.com/feedback/)
. We’re dedicated to aiding you at every step!

## Related Blogs



[Pure React Charts – High-Performance Native Charts for Large Datasets](https://www.syncfusion.com/blogs/post/pure-react-chart-component)



[Boost Your React App Performance with Syncfusion Pure React Components](https://www.syncfusion.com/blogs/post/pure-react-components-vol-3-2025)



[Smooth Scrolling, Fast Saves: Boost React Spreadsheet Performance](https://www.syncfusion.com/blogs/post/boost-react-spreadsheet-performance)



[How To Render Large Datasets In React without Killing Performance](https://www.syncfusion.com/blogs/post/render-large-datasets-in-react)
