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} />
</>
);
}
The React useCallback Hook returns a memoized callback function. Think of memoization as caching a value so that it does not need to be recalculated. This allows us to isolate resource intensive functions so that they will not automatically run on every render.
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 (e.g. shouldComponentUpdate ).
Memoization Using Memo 1 2 3 const MyComponent = React. memo(function MyComponent(props) { /* render using props */ }); We can make a custom comparison function and pass it as a second argument to memo to control its default comparison behavior, which shallowly compares complex objects in the props object.
The goal is to return the same function instance when the factory is called with the same parameter.
I think what you want is something like this:
let memoizedcb = React.useCallback(
memoize((fieldName) => (val) => doSomething(fieldName, val)),
[]
);
where
import memoize from "fast-memoize";
Now function returned by memoizedcb
will be same across renders for the same argument.
Now you can use it like
<TextField onChange={memoizedcb("name")} value={name}/>
<TextField onChange={memoizedcb("surname")} value={surname}}/>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With