3 KiB
tags | ||||
---|---|---|---|---|
|
useCallback
In short, 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.
useCallback
returns a memoized version of the callback function it is passed. This means that the function object returned from useCallback will be the same between re-renders.
Remember that in JavaScript, functions are objects. As a result, every time a component containing a function re-renders, it creates a new instance of the function in memory. If a function doesn't change in response to a UI event throughout the lifecycle of the component, there is no need for it to rerender, hence useCallback
should be applied.
Given the same dependency value, the
useCallback
hook returns the same function instance between renderings (aka memoization).
This said, for small functions that are not intensive, it doesn't really matter if they are not memoized.
Syntax
A standard case of this would be a function that runs on a button click, for instance when sending data from a form to a server. In the example below there is quite a lot going on, and most of it is independent of the actual UI-cycle of the component.
const handleSubmit = useCallback(
async (formValues) => {
setPendSaveConfig(true);
const payload = new GenerateConfig({
workflowId: project_id,
blockId: blockId,
config: formValues,
});
axios
.post(`${process.env.REACT_APP_ENDPOINT}/save-block-config`, payload)
.then((res) => console.log(res))
.finally(() => setPendSaveConfig(false))
.catch((err) => console.error(err));
},
[blockId, project_id],
);
Note that the syntax is similar to useEffect: there is a dependency array. The effect is the same: the function contained within useCallback
will only re-rendered if one of these dependencies changes. However (see next section) the function will run in its memoized form on every click.
Reference versus result
useCallback
only memoizes the function object (the reference) not the value that is returned by the function (the result).
In the example below, the calculatePi()
function reference will not change between renders but each time the click event fires, the value returned by calculatePi()
will change. In other words, it will be assigned fresh memory.
function ParentComponent() {
const onHandleClick = useCallback(() => {
const special = calculatePi();
});
return <SubComponent handleClick={onHandleClick} />;
}
Use cases
You should not apply useCallback
in a blanket fashion, this can reduce performance rather than improve it. The best scenarios are:
-
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])
-
When the function has some internal state, e.g. when the function is debounced or throttled.