DEV Community

Cover image for 🔗 useCallback: Keeping Your Functions in Check, Literally 🧑‍💻
Akash Kumawat
Akash Kumawat

Posted on

🔗 useCallback: Keeping Your Functions in Check, Literally 🧑‍💻

You know the feeling — everything’s working great, but then, your app starts slowing down, and you’re left wondering: "Why, React? Why are you like this?" One of the sneaky culprits? React loves recreating functions every time it re-renders a component, like it’s some kind of hobby. But here comes useCallback to save the day, keeping your functions in line and making sure React doesn’t go overboard.

Let’s break it down!


Why Does React Keep Making New Functions? 🤔

So here’s the deal. Every time React re-renders a component, it creates brand-new functions — even if those functions do the exact same thing as before. This happens because React doesn’t remember function references between renders. Every single time React runs, it's like, "Let’s create a fresh one, just in case."

Seems harmless, right? But it can cause performance issues, especially when you’re passing those new functions down to child components. React thinks, “Oh, new function? Must be new props!”—and then it triggers a re-render of the child component, even when it doesn’t need to.


Enter useCallback: Your Function Stabilizer 🦸‍♂️

This is where useCallback steps in like the hero React didn’t know it needed. Instead of creating new functions on every render, useCallback lets you say, “Hey React, unless something important changes, just keep using the same function.”

How does it work? Like this:

const myFunction = useCallback(() => {
  // Logic goes here
}, [dependencies]);
Enter fullscreen mode Exit fullscreen mode

That dependencies array is the key. React will only create a new version of the function if something in the array changes. Otherwise, it’s the same trusty function from the last render. Smooth sailing from here! 🚢


When to Call in useCallback ⚙️

Here are some situations where useCallback really shines:

1. Passing Functions to Child Components

If you’re passing a function as a prop to a child component, React will recreate that function every time the parent re-renders. This means the child component will also re-render because React thinks the function prop is different.

With useCallback, you can tell React to use the same function unless something in the dependencies changes. No more unnecessary re-renders for your child components!

2. Memoized Components + Functions

Using React.memo to prevent unnecessary re-renders? Awesome. But if you’re passing a fresh function every time, you’re still gonna get those re-renders. Pair React.memo with useCallback, and you’ve got yourself a super team to block unwanted re-renders.

3. Event Handlers that Depend on State

Imagine you’ve got a button with an onClick handler that relies on state. Normally, React will recreate that handler every time the state updates. But with useCallback, you’re telling React, “Hey, no need to recreate this function unless the state it depends on actually changes.”


But Don’t Go Overboard 🚨

Now, before you wrap useCallback around every function like it’s the answer to life, the universe, and everything, here’s the deal: Not every function needs it. If your component doesn’t re-render often or the performance gains are negligible, you’re better off skipping useCallback. In fact, using it unnecessarily can make your code more complicated and even take up extra memory.

Use it where it counts — like with functions that are passed down as props or in heavy components that need some optimization love.


Wrapping it Up 🎁

At the end of the day, useCallback is like your function stabilizer, making sure React doesn’t go wild recreating functions on every render. It’s a lifesaver when you’re trying to optimize performance, but it’s not a tool for every situation. Use it where it makes sense, and your app will thank you for it.

With useCallback, your functions don’t have to be the cause of unnecessary re-renders anymore. It’s time to get those functions under control! 😎


Happy Coding :))

Top comments (1)

Collapse
 
sayed_suhaibiliyas_b7adc profile image
Sayed Suhaib Iliyas

Great explanation !! Adding sample code for better understanding.

1. Passing Functions to Child Components

import { useCallback } from 'react';

function Parent() {
  const handleClick = useCallback(() => {
    console.log("Button clicked");
  }, []); // Dependencies: Only re-create the function if these values change

  return <Child onClick={handleClick} />;
}

Enter fullscreen mode Exit fullscreen mode

2. Memoized Components + Functions

import React, { useCallback } from 'react';

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

function Parent() {
  const handleClick = useCallback(() => {
    console.log("Button clicked");
  }, []); // No dependencies, so `handleClick` will remain the same across renders

  return <Child onClick={handleClick} />;
}

Enter fullscreen mode Exit fullscreen mode

3. Event Handlers that Depend on State

import { useState, useCallback } from 'react';

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

  const handleClick = useCallback(() => {
    console.log(`Current count: ${count}`);
    setCount(count + 1);
  }, [count]); // Recreate the function only when `count` changes

  return <button onClick={handleClick}>Increment</button>;
}

Enter fullscreen mode Exit fullscreen mode