React useCallback hook notes

MilkJar
2 min readDec 5, 2020

--

Recently I received a comment on a blog post. It suggested that the event handler functions in my code example should be wrapped with useCallback. This seems to be a common misconception.

To really get our heads around useCallback lets look at something tangible.

The following is a very simple form component. It has some basic state and event handlers for updating that state when the inputs change. Take a minute to quickly scan through it. (Please note that this code deliberately misuses useCallback).

The common misconception here is that wrapping updateField in useCallback will improve performance. It will not. In fact, if we are splitting hairs, it will do the opposite.

Maybe it is easier to get a feel for if we write the code like so.

const updateField = (ev) => {
const { name, value } = ev.currentTarget;
setFormState((current) => ({ ...current, [name]: value }));
};
const updateFieldCallback = useCallback(updateField, []);

As you can clearly see here the code will create 1 function each render plus 1 memoized function. In this scenario it would be better not using useCallback at all.

Intermission: To make the code behave as was probably intended, you would need to use useMemo . useMemo accepts a factory function and caches the result. But still, in this example, the performance benefits would be negligible and the added complexity would probably make it not worth while.

const updateField = useMemo(() => {
return (ev) => {
const { name, value } = ev.currentTarget;
setFormState((current) => ({ ...current, [name]: value }));
};
}, [setFormState]);

So how would we actually implement our Form component if we wanted to make use of useCallback ?

Using useCallback here allows React.memo to perform a referential equality check on the onUpdate prop. This prevents the SimpleFormField component from being re-rendered each time the state of SimpleForm updates — exactly as described in the hook documentation.

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…

This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders

What is great about having a clear understanding of how useCallback works is that it can actually help us write better code. It pushes in the direction of efficient abstractions by making us think about the referential quality of our child components.

Have I missed something? Is something not clear? Please let me know.

--

--