'onCallback React Hook - best practice query
This is more of a best practice question than anything. But I'm wondering if any inline functions within a React component should typically be wrapped with an onCallback for performance? Under what circumstances would I not wrap a function like this with onCallback?
For example:
const ToolSearch = (props: ToolsSearchProps) => {
const handleOnClick = () => {
alert('do something');
}
return (
<NoCardOverflow>
<ToolSearchCollapse openState={filtersOpen} onClick={handleOnClick} />
</NoCardOverflow>
);
};
In this example should I be doing this:
const ToolSearch = (props: ToolsSearchProps) => {
const handleOnClick = useCallback(() => {
alert('do something');
},[]);
return (
<NoCardOverflow>
<ToolSearchCollapse openState={filtersOpen} onClick={handleOnClick} />
</NoCardOverflow>
);
};
Solution 1:[1]
I will try to explain the useCallBack with an example. We all know the definition of useCallBack, but when to use is the trick part here. So, let me take an example.
const RenderText = React.memo(({ text }) => {
console.log(`Render ${text}`);
return (<div>{text}</div>);
});
const App = () => {
const [count, updateCount] = React.useState(0);
const [lists, updateLists] = React.useState(["list 1"]);
const addListItem = () => {
updateLists([...lists, "random"]);
};
return (
<div>
{`Current Count is ${count}`}
<button onClick={() => updateCount((prev) => prev + 1)}>Update Count</button>
{lists.map((item: string, index: number) => (
<RenderText key={index} text={item} />
))}
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#app"));
jsFiddle: https://jsfiddle.net/enyctuLp/1/
In the above, there are two states.
- count - Number
- lists - Array
The RenderText just renders the text passed to it. (which is the item in the list). If you click on the Update Count button, the RenderText will not re-render because it is independent from the main component (App) and during the updateCount, only the App component will re-render, since it needs to update the count value.
Now, pass the addListItem into RenderText component and click on the Update Count button and see what happens.
jsFiddle: https://jsfiddle.net/enyctuLp/2/
You can see that the RenderText will re-render even though there is no change in the list array and this is BECAUSE :
- when the count is updated, the
Appwill re-render - which, will re-render the
addListItem - which causes the
RenderTextto re-render.
To avoid this, we should use useCallBack hook.
jsFiddle: https://jsfiddle.net/enyctuLp/4/
Now, the addListItem function has been memoized and it will only change when the dependency are changed.
Solution 2:[2]
ToolSearchCollapse is your child component, and so wrapping your handleClick function makes sense. Coz every time the parent component re-renders a new reference of handleclick will be passed to ToolSearchCollapse, and your child will re-render every time a parent state changes, (from states that aren't passed to the child also). Using useCallback will not allow creating new references, and thus you can control when your child should render.
If you don't pass this fn to the child, I don't see any reason to use useCallback.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
