I want to know if this is ok to do:
const onClick = useCallback(() => {
setHoveredItem((hovered)=>{
setSelectedData(hovered);
return hovered;
});
},[]);
Just using the useState setter to get the value, 'removing' the useCallback's dependency to avoid re-renders. Notice the empty dependency array. Is that an acceptable way to do things?
I am working on an application with an chart. Re-renders to the chart are really bad (they basically break). So I used React.memo. All good until I needed to pass event listeners:
<MemoChart
{...{ data, onHover, onClick }}>
</MemoChart>
On click, I want to store the hovered item.
Initially, this was my onClick function:
const onClick = () => {
setSelectedData(hoveredItem);
}
But of course, every hover event re-renders the parent, which causes the chart to re-render. Use Callback on onClick would work, except that if I add a dependency on hoveredItem, it will do nothing.
So this is what I did. It works, but I have never seen it done and I wonder if it is ok to do:
const onClick = useCallback(() => {
setHoveredItem((hovered)=>{
setSelectedData(hovered);
return hovered;
});
},[]);
UPDATED
For your case, I think you can use useRef to keep hovered data instead of a state.
const hoveredRef = React.useRef()
const onHover = useCallback((hovered) => {
hoveredRef.current = hovered //won't make re-rendering
}, [])
const onClick = useCallback(() => {
setSelectedData(hoveredRef.current)
}, [])
OLD ANSWER
<MemoChart
{...{ data, onHover, onClick }}>
</MemoChart>
Your problem is these events onHover and onClick will be initialized newly every time which will cause unexpected renderings. If you have a complex computation on your component, it will be laggy on renderings.
In your onClick case with useCallback, it works but it will create more trouble in state handlers because it's lacking dependencies which means it's considering that function will be the same all the time, but in fact, your function is relying on hovered state should be changed on events.
I'd propose that you should pass dependencies to align with your used states in functions.
const onHover = useCallback((hovered) => {
setHoveredItem(hovered);
}, []) //only initialize this function once after the component mounted
const onClick = useCallback(() => {
setSelectedData(hovered);
},[hovered]); //initialize this function again when `hovered` state changes
const onClick = useCallback(() => {
setHoveredItem((hovered)=>{
setSelectedData(hovered);
return hovered;
});
},[]);
This is not wrong, but it's a hack that helps you getting the fresh state if you have closures issues. In your case there is no reason since you are not working with closures, and the problem is just that you set an empty deps array. Now you say that this does not work:
const onClick = useCallback(() => {
setSelectedData(hovered);
},[hovered]);
But this MUST work, unless you are doing something wrong when you call setHovereditem.