React useCallback
Tags: react, javascript, webdev, tutorial
Published: 2023-06-07 23:57:44

**Dead Simple Chat allows you to easily add Chat to any React Application using powerful** [**Javascript Chat SDK**](https://deadsimplechat.com/javascript-chat-api-sdk)**.**

In this blog post, we will see how to use React `useCallback` hook. 

What is its purpose we will also look at real-world scenarios where it should be used and common pitfalls to avoid when using React `useCallback`.

## What is useCallback?

`useCallback` is used to cache the function "definition". It is typically used in conjunction with [React `memo`](https://deadsimplechat.com/blog/memo-vs-usememo-in-react/). 

If you have cached your component with React memo so that it doesn't re-render unless its props are changed.  

If you are passing the component a function, then your function will re-render every time, defeating the purpose of using memo.

Because in Javascript `function() {}` or `() => { }` create a different function, thus the prop will never be the same, causing the component to re-render, make `memo` useless.

To prevent this from happening, you can wrap the function definition inside `useCallback` and this prevents the re-creation of the function unless the dependencies changes.

## Syntax of `useCallback`

The `useCallback` hook accepts two parameters, one is the method/function you would like to cache, and the second parameter is the dependency array, which returns the cached function.

When the variable passed in the dependency array changes, the `useCallback` hook returns and updated function.

```js
const method = useCallback(<METHOD>, [<DEPENDENCY_ARRAY]);
```

Let's look at some examples to understand it better.

**Dead Simple Chat allows you to integrate chat in minutes into your React or web application using the powerful** [**Javascript Chat SDK.**](https://deadsimplechat.com/javascript-chat-api-sdk)

## Example of `useCallback`

Consider the following code:

```js
import React, { useState } from "react";

const ChildComponent = React.memo(({ onButtonClick }) => {
  console.log("ChildComponent rendered");
  return <button onClick={onButtonClick}>Increment</button>;
});

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

  const [theme, setTheme] = useState("light");

  const handleButtonClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <h1>Current Theme: {theme}</h1>
      <button
        onClick={() => {
          theme === "light" ? setTheme("dark") : setTheme("light");
        }}
      >
        Toggle Theme
      </button>
      <h1>Counter: {count}</h1>
      <ChildComponent onButtonClick={handleButtonClick} />
    </div>
  );
}

export default ParentComponent;
```

In the above code we have created a `ChildComponent` and cached it using `React. memo`.

***A Brief  Primer on React Memo:***

*By using* `React.memo` the component will not re-render unless the props are changed.

*Typically when the parent component is re-rendered all the child components are also re-rendered.*

*But by using React memo on a child component, when the parent component is re-rendered the Child Component is not re-rendered unless the props of the Child Component change.*

In the `ChildComponent` we have also added a `console.log` statement to print on the console "ChildComponent rendered".

We are accepting a method `onButtonClick` as a prop of the `ChildComponent` and calling the method on the `onClick` event when the button is pressed.

Next, we have created a `ParentComponent` and in the `ParentComponent`we have created two state variables, one is `count` and another one is `theme`.

In the `ParentComponent` we have created a method called as `handleButtonClick` which increments our state variable `count` and we are passing this method as a prop to the `ChildComponent`.

We have also created a button to toggle the second state variable called as the `theme` and when the button is pressed we are toggling the theme from light to dark.

We are also displaying the current theme and count.

As we have used React.memo our expected behaviour would be when we toggle the theme, we should not see, the `ChildComponent rendered`message on the screen.

Let's try it out:

As you can see in the above video, each time the "Toggle Theme" button is pressed the "ChildComponent rendered" message is printed on the screen.

Why is this happening? 

We had discussed in the React useCallback intro, it happens because JavaScript creates a new function each time it the component is rendered.

And we are passing the function as a prop, and React memo will see it as a new function and re-render the child component.

To solve this problem we will wrap our function in `useCallback` and it will return a cached version of our function.

Here is the updated code:

```js
import React, { useState, useCallback } from "react";

const ChildComponent = React.memo(({ onButtonClick }) => {
  console.log("ChildComponent rendered");
  return <button onClick={onButtonClick}>Click me</button>;
});

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

  const [theme, setTheme] = useState("light");

  // Using useCallback to cache the function
  const handleButtonClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return (
    <div>
      <h1>Current Theme: {theme}</h1>
      <button
        onClick={() => {
          theme === "light" ? setTheme("dark") : setTheme("light");
        }}
      >
        Toggle Theme
      </button>
      <h1>Counter: {count}</h1>
      <ChildComponent onButtonClick={handleButtonClick} />
    </div>
  );
}

export default ParentComponent;
```

In our updated code we have wrapped the function inside `useCallback`hook. Let's see how our updated code performs:

As you can see in the console, the "ChildComponent rendered" message is not printed each time we press the "Toggle Theme" button.

Let's look at some real-world scenarios where to use `useCallback`.

## Real-world Scenarios of `useCallback`

We will look at some real-world scenarios with code examples where using `useCallback` would be useful.

### Memoize Data Fetching Function in Infinite Scroll

In an infinite scrolling list we can use the `useCallback` to cache the function that is responsible to fetch the data to prevent unnecessary renders and API calls.

Let's look at an example, we will build a simple infinite scrolling listing using the freely available Github List Users API.

In this example, we will use `useCallback` and `useEffect` in conjunction to prevent unnecessary API calls.

```js
import { useState, useEffect, useCallback } from "react";

function InfinitUserScroll() {
  const [users, setUsers] = useState([]);
  const [page, setPage] = useState(1);

  const fetchData = useCallback(async () => {
    const response = await fetch(
      `https://api.github.com/users?since=${page * 30}`
    );
    const nextData = await response.json();
    setUsers((curData) => [...curData, ...nextData]);
  }, [page]);

  useEffect(() => {
    fetchData();
  }, [fetchData]);

  const handleScroll = (e) => {
    const { scrollTop, scrollHeight, clientHeight } = e.target;
    if (scrollHeight - scrollTop === clientHeight) {
      setPage((prevPage) => prevPage + 1);
    }
  }

  return (
    <>
      <div
        onScroll={handleScroll}
        style={{ overflowY: "scroll", height: "400px" }}
      >
        <h1>Github Users</h1>
        <hr />
        {users.map((item, index) => (
          <div key={index}>{item.login}</div>
        ))}
      </div>
    </>
  );
}

export default InfinitUserScroll;
```

In the above code we are using `useCallback` to cache  methods  one is the `fetchData` method.

The `fetchData` method is cached and `page` state variable is the dependency passed to the useCallback caching the fetchData method.

Then we are passing `fetchData` as a dependency in `useEffect` and calling `fetchData` method in `useEffect`.

Listing `page` as a dependency in `useCallback` causes the `fetchData`method to be re-created only when the `page` value changes.

When the `page` value changes, new `fetchData` method is created which causes the `useEffect` to trigger to fetch the new page info.

Util the page value is updated the `fetchData` method is not called thus preventing unnecessary API calls.

### Debouncing Input to prevent excessive calls to API

We can also use `useCallback` hook to debounce the user input that calls an API for example a search API.

Debouncing the user input prevent excessive calls to the API, thus preventing the server from being overloaded or if you are using a 3rd party API prevents the API costs.

Let's code at the code example:

```js
import React, { useState, useEffect, useCallback } from "react";
import debounce from "lodash.debounce";

function WikiSearch({ searchDelay = 300 }) {
  const [searchTerm, setSearchTerm] = useState("");
  const [results, setResults] = useState([]);

  const performSearch = async (term) => {
    const response = await fetch(
      `https://en.wikipedia.org/w/api.php?action=query&list=search&format=json&origin=*&srsearch=${term}`
    );
    const data = await response.json();
    setResults(data.query.search);
  };

  const debouncedSearch = useCallback(
    debounce((term) => {
      performSearch(term);
    }, searchDelay),
    [searchDelay]
  );

  useEffect(() => {
    if (searchTerm) {
      debouncedSearch(searchTerm);
    } else {
      setResults([]);
    }
  }, [searchTerm, debouncedSearch]);

  return (
    <div>
      <h3>Wiki Search Engine</h3>
      <hr />
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        placeholder="Search Wikipedia."
      />
      {results.length > 0 ? (
        <h1>Wikipedia Search Results</h1>
      ) : (
        <div>
          <br />
          <strong>Nothing Found</strong>
        </div>
      )}
      <ul>
        {results.map((result) => (
          <li key={result.pageid}>
            <a
              href={`https://en.wikipedia.org/?curid=${result.pageid}`}
              target="_blank"
              rel="noreferrer"
            >
              {result.title}
            </a>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default WikiSearch;
```

In the above code example, we are using the Wikipedia API to search Wikipedia to fetch a list of pages.

We are adding a default debounce of 300ms and using the `lodash.debounce` library and creating a cached de-bounced method using `useCallback`

We have created a `WikiSearch` component that accepts an optional `searchDelay`.

Next, we have created a method called as `performSearch`, the method will fetch the information and set it in the results date variable.

Using the `useCallback`, we have created a debouncedSearch method and called the `useCallback` hook to `debounce` the search.

Then finally in the `useEffect` hook we are calling the `debouncedSearch`method.

Here is the demo:

### Handling Events in a List

When you a list of items with each item has an event handler, then we can use the `useCallback` to cache the handler function.

To demonstrate this we will create a TodoList component, that will display a list of Todos.

```js
import React, { useState, useCallback } from "react";

const TodoItem = React.memo(({ item, onToggle }) => (
  <li>
    <input
      type="checkbox"
      checked={item.completed}
      onChange={() => onToggle(item.id)}
    />
    {item.name}
  </li>
));

function TodoListComponent() {
  const [todos, setTodos] = useState([
    { id: 1, name: "Todo 1", completed: false },
    { id: 2, name: "Todo 2", completed: false },
    { id: 3, name: "Todo 3", completed: false },
    { id: 4, name: "Todo 4", completed: false }
  ]);

  const handleToggle = useCallback((id) => {
    setTodos((prevTodos) =>
      prevTodos.map((todo) =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  }, []);

  return (
    <ul>
      {todos.map((todo) => (
        <TodoItem key={todo.id} item={todo} onToggle={handleToggle} />
      ))}
    </ul>
  );
}

export default TodoListComponent;
```

We have created the `handleToggle` method and caching it using `useCallback`, and passing the `handleToggle` to the `<TodoItem />` list.

## Conclusion

In this blog post, we have learned how to use `useCallback` and the real-world scenarios and examples.