The Button receives the onClose method as a prop, which is used in the handleKeyDown. The handleKeyDown itself is added as a dependency for the useEffect, therefore I wrapped it with a useCallback so in case I want to add a state change in the future in the useEffect, this won't cause an infinite rerender. However, I have to add the onClose in the dependency of the useCallback wrapped around the handleKeyDown. As the onClose comes from props, this will change at every re-render, causing the handleKeyDown to be created at every re-render. Of course, a solution would be to wrap the onClose in a useCallback before passing it to the CloseButton and use React.memo on the component. However, I want to handle this from inside the component instead of outsourcing it. Could you please confirm if there is a way to solve this issue? Thank you.
const Button: React.FC<ButtonProps> = ({ onClose ...props }) => {
const onKeyDown = (event: KeyboardEvent) => {
if (event.keyCode === 65) {
onClose(event);
}
};
useEffect(() => {
window.addEventListener('keydown', onKeyDown);
return () => {
window.removeEventListener('keydown', onKeyDown);
};
}, [onKeyDown]); `
...
}
CodePudding user response:
There's no way to determine if the onClose
function has changed since previous render if it is not being memoized by the parent that defines it.
You can either assume that this function shouldn't change between renders and just not add it to the dependency array or you will have to tun that useEffect on every render (omit the dependency array altogether).
CodePudding user response:
The official way to solve this is to use useCallback
in the parent like you said (but there is no need to use React.memo though) and I would recommend doing it this way to avoid bugs.
If you have some good reason for not using this approach, you could do it by useRef
like this:
const Button: React.FC<ButtonProps> = ({ onClose ...props }) => {
const onCloseRef = useRef(onClose);
onCloseRef.current = onClose;
const onKeyDown = useCallback((event: KeyboardEvent) => {
if (event.keyCode === 65) {
onCloseRef.current(event);
}
}, [onCloseRef];
useEffect(() => {
window.addEventListener('keydown', onKeyDown);
return () => {
window.removeEventListener('keydown', onKeyDown);
};
}, [onKeyDown]);
...
}
But this approach has some pitfalls so I would avoid it if possible.