When passing a callback function, especially when passing a parameterized function, I know that I should use the useCallback
hook because the use of anonymous functions can adversely affect performance.
the example of anonymous function I said is like this.
import React, { useState } from 'react';
const Component = () => {
const [param, setParam] = useState('');
...
return (
...
<SomeComponent
onClick={() => setParam('parameter')}
{...others}
/>
);
}
In the process of converting an anonymous function to use this hook, I encountered an error saying 'Too many renders' or it didn't work properly. But I don't know exactly in what situation and in what situation.
and I used useCallback
like below.
import React, { useState, useCallback } from 'react';
const Component = () => {
const [param, setParam] = useState('');
const handleClick = useCallback((params) => {
setParam(params);
},[]);
...
return (
...
<SomeComponent
onClick={handleClick('parameter')}
{...others}
/>
);
}
However, when using an anonymous function to return within useCallback
, it also worked.
This means code like here. (Only the differences pared to the code above.)
const handleClick = useCallback((params) => {
return () => setParam(params);
},[]);
In this situation, I wonder if it's worse than just using an anonymous function inside the useCallback
if I simply use an anonymous function instead of using this hook.
When passing a callback function, especially when passing a parameterized function, I know that I should use the useCallback
hook because the use of anonymous functions can adversely affect performance.
the example of anonymous function I said is like this.
import React, { useState } from 'react';
const Component = () => {
const [param, setParam] = useState('');
...
return (
...
<SomeComponent
onClick={() => setParam('parameter')}
{...others}
/>
);
}
In the process of converting an anonymous function to use this hook, I encountered an error saying 'Too many renders' or it didn't work properly. But I don't know exactly in what situation and in what situation.
and I used useCallback
like below.
import React, { useState, useCallback } from 'react';
const Component = () => {
const [param, setParam] = useState('');
const handleClick = useCallback((params) => {
setParam(params);
},[]);
...
return (
...
<SomeComponent
onClick={handleClick('parameter')}
{...others}
/>
);
}
However, when using an anonymous function to return within useCallback
, it also worked.
This means code like here. (Only the differences pared to the code above.)
const handleClick = useCallback((params) => {
return () => setParam(params);
},[]);
In this situation, I wonder if it's worse than just using an anonymous function inside the useCallback
if I simply use an anonymous function instead of using this hook.
-
If your function is as small as that, I doubt you'll gain any performance by even using
useCallback
.. But you loose a lot of readability instead, when ->onClick={(params) => setParam(params)}
would do the same. – Keith Commented Jan 24, 2020 at 10:40 -
@Keith thanks for the ment. But that code is just an example. Maybe it was an inappropriate example. And the point of my question was, "Is the same performance penalty when returning anonymous functions within` useCallback` just using anonymous functions? " In other words, people remend using
useCallback
because anonymous functions have a negative impact on performance, and I wonder if I can use them if they work correctly when I return an anonymous function inside` useCallback`. – Anthony Min Commented Jan 24, 2020 at 10:58 -
use
useCallback
when you need to optimize child re-renders by passing the same reference of the function and the child ponent isPureComponent
/wrapped withReact.memo
, although it remembers the previous function reference a new function is getting created on every re-render but gets garbage collected. – Asaf Aviv Commented Jan 24, 2020 at 11:42 - Okay. Thanks for the ment. It helped a lot! – Anthony Min Commented Jan 24, 2020 at 12:38
3 Answers
Reset to default 5 const handleClick = useCallback((params) => {
setParam(params);
},[]);
...
return (
...
<SomeComponent
onClick={handleClick('parameter')}
{...others}
/>
);
in the above code during first render, at this statement "onClick={handleClick('parameter')}"
handleClick function is called with a string named "parameter". since handleClick has setParam("parameter"), it will update state. updating state will cause re-render which will again e to same statement "onClick={handleClick('parameter')}"
causing infinte loop.
following code you added later works because you are not updating state, instead returning a function, which acts as onclick handler.
const handleClick = useCallback((params) => {
return () => setParam(params);
},[]);
better way of doing this shoud be as follwing,
import React, { useState, useCallback } from 'react';
const Component = () => {
const [param, setParam] = useState('');
const handleClick = useCallback((params) => {
setParam(params);
},[]);
...
return (
...
<SomeComponent
onClick={handleClick}
{...others}
/>
);
}
ing back to your question , paring performance depends on the other function definitions and render times of child poents inside return function inside your Compoenent. let's say you have another onclickHanldier named 'anotherHandleClick' inside your app. then your ponent looks like this
const Component = () => {
const [param, setParam] = useState('');
const [anotherParam, setAnotherParam] = useState('');
const handleClick = (params) => {
setParam(params);
};
const anotherHandleClick =(params) => {
setAnotherParam(params);
};
...
return (
...
<SomeComponent
onClick={handleClick('parameter')}
{...others}
/>
<SomeComponent
onClick={antherHandleClick('parameter')}
{...others}
/>
);
}
in the above ponent when any one of "SomeCompoenent" clicked entiere "Component" re-renders, so the handler functions are newly defined.and when both 's does referential equality check on onclick handler functions, they believe it is new handler function which casues them to render both. in that case it is better to use useCallBack hook as following,
const Component = () => {
const [param, setParam] = useState('');
const [anotherParam, setAnotherParam] = useState('');
const handleClick = useCallback((params) => {
setParam(params);
},[]);
const anotherHandleClick = useCallback((params) => {
setAnotherParam(params);
},[]);
...
return (
...
<SomeComponent
onClick={handleClick('parameter')}
{...others}
/>
<SomeComponent
onClick={antherHandleClick('parameter')}
{...others}
/>
);
}
in the above code when any one is clicked , state changes. then when rendering, useCallback make sure that onclick handler refernces didn't change. so having dependency of onclick handlers won't be rerendered.
so final thought is It's creating a function on each render in both cases. the second (because it'e wrapped in a useCallback) will return the memoized function created on the initial render
when to use useMemo or useCallback refer this
In your code:
const handleClick = useCallback((params) => {
setParam(params);
},[]);
You should pass the parameters into the second argment of useCallback
like so:
const handleClick = useCallback((params) => {
setParam(params);
},[setParam,param, SETPARAMACTUALVALUE]);
// Change SETPARAMACTUALVALUE to whatever the variable name is for the `setParam` hook
useCallback hook usage will be better if you want to save the function until hook's dependency changed. It gives you better performance because the hook remember internal function.