I am wondering how best to return a memoized callback function in React when using a factory to generate that callback. The goal is to return the same function instance when the factory is called with the same parameter.
For example:
function MyComponent() {
// This will always return a new function
const makeCallback = param => () => {
/* perform action with 'param' */
};
return (
<>
<Button onClick={makeCallback('foo')} />
<Button onClick={makeCallback('bar')} />
<Button onClick={makeCallback('baz')} />
</>
);
I don't believe wrapping the factory itself with a useCallback would provide any benefit, since that function is not actually being passed to any of the children, so my idea was to return a useCallback function from the factory.
Like this:
const makeCallback = param => React.useCallback(
() => {
/* perform action with 'param' */
},
[param]
);
However, that was not allowed and failed at build time.
React Hook "React.useCallback" is called in function "makeCallback" which is neither a React function component or a custom React Hook function - react-hooks/rules-of-hooks
The "Rules of Hooks" say clearly that calling a hook in a nested function is not permitted, but this seems odd to me since a custom hook is often literally just a function that calls other hooks. It says the primary concern is preserving the order of execution, but I don't think that would be violated in this case.
Is my best option to turn my factory into a hook and call it explicitly at the top level for each case? I'd prefer the simplicity of building the callback in the button itself, since it's a little less typing and the param piece is more apparent and obvious when kept with the button.
// Same factory function, but now it's magically a "hook"
const useCallbackFactory = param => {
return React.useCallback(() => { /* do 'param' stuff */ }, [param]);
}
function MyComponent() {
// Define the callbacks ahead of time
const fooCb = useCallbackFactory('foo');
const barCb = useCallbackFactory('bar');
const bazCb = useCallbackFactory('baz');
return (
<>
<Button onClick={fooCb} />
<Button onClick={barCb} />
<Button onClick={bazCb} />
</>
);
}