Before talking about use callback hook we need to talk extensively about the functions in javascript.
Functions in JavaScript are first-class functions meaning that a function is a regular object.
- The Object can be passed to function as a parameter.
- The function object can be returned by other functions
- The functions are compared like normal Object
as we know functions are just special Objects in Javascript and an object (including a function object) equals only itself.
Let me give you an example
function createFunction() {
return (a, b) => a + b;
}
const sum1 = createFunction();
const sum2 = createFunction();
sum1(1, 2); // => 3
sum2(1, 2); // => 3
sum1 === sum2; // => false
sum1 === sum1; // => true
sum1 and sum2 are functions that sum two numbers. They’ve been created by the factory() function. because they refer to the different memory location
what does use callback do?
UseCallback Returns a memoized callback.
we pass a callback function and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed.
When to use useCallback() ?
function MyComponent() {
// clickHandler is re-created on each render
const clickHandler = () => {
console.log('Clicked!');
};
// ...
}
As we have seen earlier clickHandler point's to different memory locations every time we re-renders because a brand new function is created every time we re-renders
and this re-creation of function doesn't really affect in small apps but in bigger apps, this could cause performance issues. although it doesn't mean you need to memoize each and every function remember memoization is a trade-off between space-complexity and time-complexity of code. so use it carefully
But in some cases you need to maintain the same instance between renderings for example :
A functional component wrapped inside React.memo() accepts a function object prop
When the function object is a dependency to other hooks, e.g. useEffect(..., [callback])
We can memorize the clickHandler using useCallback like this
import { useCallback } from 'react';
function MyComponent() {
// clickHandler is the same function object
const clickHandler = useCallback(() => {
console.log('Clicked!');
}, []);
// ...
}
clickHandler variable has always the same callback function object between renderings of MyComponent.
let's explain use callback with some examples
This useCallback hook really shines when you have a component with a child frequently re-rendering, and because pass a callback to it.
import React, { useState, useCallback } from 'react'
const funccount = new Set();
const App = () => {
const [count, setCount] = useState(0)
const [number, setNumber] = useState(0)
const incrementCounter = () => {
setCount(count + 1)
}
const decrementCounter = () => {
setCount(count - 1)
}
const incrementNumber = () => {
setNumber(number + 1)
}
funccount.add(incrementCounter);
funccount.add(decrementCounter);
funccount.add(incrementNumber);
alert(funccount.size);
return (
<div>
Count: {count}
<button onClick={incrementCounter}>
Increase counter
</button>
<button onClick={decrementCounter}>
Decrease Counter
</button>
<button onClick={incrementNumber}>
increase number
</button>
</div>
)
}
export default App;
The problem with the above code is all three functions are recreated again. The alert increases by three at a time but if we update some states all the functions related to that states should only re-instantiated. If another state value is unchanged, it should not be touched.
Let's try to solve this via useCallback()
import React, { useState, useCallback } from 'react'
var funccount = new Set();
const App = () => {
const [count, setCount] = useState(0)
const [number, setNumber] = useState(0)
const incrementCounter = useCallback(() => {
setCount(count + 1)
}, [count])
const decrementCounter = useCallback(() => {
setCount(count - 1)
}, [count])
const incrementNumber = useCallback(() => {
setNumber(number + 1)
}, [number])
funccount.add(incrementCounter);
funccount.add(decrementCounter);
funccount.add(incrementNumber);
alert(funccount.size);
return (
<div>
Count: {count}
<button onClick={incrementCounter}>
Increase counter
</button>
<button onClick={decrementCounter}>
Decrease Counter
</button>
<button onClick={incrementNumber}>
increase number
</button>
</div>
)
}
export default App;
when use useCallback to memorize the function and if we change the state ‘count’ then two functions will be re-instantiated so the set size will increase by 2 and when we update the state ‘name’ then only one function will be re-instantiated and the size of the set will increase by only one.
this might seems unnecessary right now thinking of just 3 functions but think of this thing when there are thousands of functions and they are passing as props to hundreds of components think of how big of a performance issue would this be then and we will use useCallback hook to optimize the re-creating of functions then.
useCallback vs useMemo
Conclusion
React's useCallback hook is used to wrap functions. It tells React to not re-create a wrapped function when a component re-renders, unless any of the useCallback's dependencies change.