I'm making a pixel drawing app.
I have a canvas component which handles the drawing logic,
inside of which is the following useEffect:
...
useEffect(() => {
// Pass reference of the canvas element to the parent component
setCanvasRef(canvasRef.current);
// Mouse util -> add listeners and offset the coordinates relative to the drawing canvas element
mouse.follow(canvasRef.current);
// Add listeners (not using synthetic react listeners because I need to track events
// outside the canvas bounds)
document.addEventListener('mousedown', executeCurrentState);
document.addEventListener('mouseup', executeCurrentState);
document.addEventListener('mousemove', executeCurrentState);
// Render initial pixel data to the canvas
render();
// Cleanup
return () => {
document.removeEventListener('mousedown', executeCurrentState);
document.removeEventListener('mouseup', executeCurrentState);
document.removeEventListener('mousemove', executeCurrentState);
mouse.removeListeners();
};
}, [setCanvasRef, mouse, render, executeCurrentState]);
...
React tells me to wrap the render
and executeCurrentState
functions inside a useCallback,
But since these functions call other functions, I have to add those to the useCallback dependency array, and wrap them in a useCallback too.
It goes on like this recuresively until every function and property called by render
and executeCurrentState
is wrapped in useCallback.
How can I avoid this?
refering a function with a ref could work
const SomeComponent = () => {
const executeCurrentState = () => {
// use a prop or state
}
const executeCurrentStateRef = useRef();
executeCurrentStateRef.current = executeCurrentState;
useEffect(() => {
...
const executeCurrentStateWrapper = (event) => {
executeCurrentStateRef.current(event) // always points to latest function
}
document.addEventListener('mousedown', executeCurrentStateWrapper);
...
return () => {
document.removeEventListener('mousedown', executeCurrentStateWrapper);
}
, [])
if you are using React 18 and want to try out their new (experimental) useEvent hook
const executeCurrentState = useEvent(() => {
// code for function in component render
});
useEffect(() => {
document.addEventListener('mousedown', executeCurrentState);
...
return () => {
document.removeEventListener('mousedown', executeCurrentState);
}
}, []) // no dependencies needed for userEvent