Skip to main content

Command Palette

Search for a command to run...

Understanding of UseCallback React Hooks In Detail

Updated
4 min read
Understanding of UseCallback React Hooks In Detail

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class. There are list of hooks which React community introduced.

The useCallback hook is used, when you have a component in which the child is re-rendering again and again without need. Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed.

JavaScript provides three different value-comparison operations:

- === Strict Equality Comparison ("strict equality", "identity", "triple equals")
- == Abstract Equality Comparison ("loose equality", "double equals")
- Object.is(value1, value2) // return boolean
e.g.
var a = 10;
    var b = 10;
    console.log(a == b); // true
    var c = 5;
    var d = "5";
    console.log(c == d); // true
    const obj1 = {
        first_name: "javascript",
    };
    const obj2 = {
        first_name: "javascript",
    };
    console.log(obj1 == obj2); // false

Now, let's understand referential equality in case of function comparison

function func() {
  return () => "JavaScriptFunc"
}
let javaScript1 = func();
let javaScript2 = func();
javaScript1 === javaScript2 // false

This is the key point to understand, when the component is re-rendered, functions defined inside function components are recreated each time, resulting in referential inequality .

Referential Equality -> We can say two objects are referentially equal when the pointers of the two objects are the same or when the operators are the same object instance.

To prevent creating the new instances of functions while re-rendering components, you can wrap the function inside useCallback. The useCallback() hook returns a memoized callback to maintain referential equality between renders of functions.

Now, let's deep dive and try to understand with react component:

import { useState, useEffect } from "react";

const Summation = () => {
  const [userInput, setUserInput] = useState("");
  const [result, setResult] = useState("");
  const [num1, num2] = [4,5];

  const sum = () => num1 + num2;

  useEffect(() => {
    console.log(`New Sum: ${sum()}`);
    setResult(sum);
  }, [sum]);

  return (
    <main className="App">
      <input
        type="text"
        placeholder="input"
        value={userInput}
        onChange={(e) => setUserInput(e.target.value)}
      />
      <h1>Output: {userInput || "--"}</h1>
       <div>{result}</div>
    </main>
  );
};
export default Summation;

Here, userInput is a state and num1 and num2 are 2 constant which are not changing. Sum is function which return total of num1 and num2. We also have useEffect which is relying on sum function as dependency, so any time sum function will change, useEffect will run.

But if you try to type on input box, you will observe, how many letters you type in the textbox, same number of times console.log printed with same value

Screenshot 2022-06-01 at 4.01.42 PM.png

We also get this warning, which state, sum function is recreating on every re-render of component and making dependency in useEffect:

Screenshot 2022-06-01 at 4.06.22 PM.png

Moving sum function inside useEffect will limit the flexibility of reusing the function, thus wrapping it inside useCallback:

const sum = useCallback(() => num1 + num2, [num1, num2]);

Weird Part start here:

Let's rebuild the component with some changes:

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

const Summation = () => {
   const [userInput, setUserInput] = useState("");
  const [result, setResult] = useState(0);
  const [num1, num2] = [4,5];

  const sum = () => num1 + num2;

    //const sum = useCallback(() => num1 + num2, [num1, num2]);
    const buildArray = () => [num1, num2];

    useEffect(() => {
        console.log(`New array: ${buildArray()}`);
        //setResult(buildArray());
    }, [buildArray]);

    return (
        <main className="App">
            <input type="text" placeholder="input" value={userInput} onChange={(e) => setUserInput(e.target.value)} />
            <h1>Output: {userInput || "--"}</h1>
            <p>Result: {JSON.stringify(result)}</p>
        </main>
    )
}

export default Summation

Here, same like previous, we will have same warning for buildArray function and useEffect will trigger when type on input box, same as earlier.

Bigger problem in this scenario is, since this is not primitive data type, React will not save us from ourselves and this is where beginners have a lots of problem because once they set the state and they have a function in the dependency that is recreated on every render. When this happens this will create endless rendering loop:

To demonstrate the real pain of this component, uncommenting setResult state inside useEffect

When user start typing in input what are the common React re-rendering mistake a developer should avoid while using useCallback hooks.

useEffect(() => {
        console.log(`New array: ${buildArray()}`);
        setResult(buildArray());
    }, [buildArray]);

!!!BOOM!!!

Screenshot 2022-06-01 at 4.58.08 PM.png

useCallback will be saviour here:

const buildArray = useCallback(() => [num1, num2], [num1, num2]);

Conclusion

Performance is really important when talking about React applications. UseCallback hooks is going to help you a lot when working with complex situations, calculations or with real big data to prevent unnecessary operations.

Thanks for reading 🙏

M

Very informative!!