I have a component with some internal state that is not being propagated throughout the application just on certain events.
const FunComponent = (initialDescription, onSave) => {
const [description, setDescription] = useState('initialDescription');
const handleDescriptionSave = () => {
onSave(description);
}
return ( ... );
}
My issue is that the component can get unmounted by something higher up in the component tree, and at that point I would want to force-trigger onSave
. Great – i thought – I can just write a useEffect with an empty dependency array, and it will be run on unmount.
The issue is that both the onSave
prop and the description
state is something my useEffect cleanup depends on.
If I add them to the dependency array, it will save with the previous description value on every description change. I don’t want that.
If I don’t add them to the dependency array, it will just use the initial onSave
and description
value on unmount.
const FunComponent = (initialDescription, onSave) => {
const [description, setDescription] = useState(initialDescription);
const handleDescriptionSave = () => {
onSave(description);
}
useEffect(() => {
return () => { onSave(description) };
}, [onSave, description])
return ( ... );
}
What hack I came up with is to store the callback in a ref, and have an effect to keep it up to date, and call the ref on unmount:
const FunComponent = (initialDescription, onSave) => {
const [description, setDescription] = useState(initialDescription);
const handleDescriptionSave = useCallback(() => {
onSave(description);
}, [onSave, description]);
const handleDescriptionSaveRef = useRef(handleDescriptionSave);
useEffect(() => {
handleDescriptionSaveRef.current = handleDescriptionSave;
}, [onSave, description]);
useEffect(() => {
return () => { handleDescriptionSaveRef.current(); };
}, []);
return ( ... );
}
But this seems super-duper hacky.
Is there a general recommended pattern for this I fail to recognise? Will this fail in some way? Is this a good solution?
2
Answers
Interesting question.
Your approach does not look as something bad. You can use special hook for it like this https://github.com/streamich/react-use/blob/master/src/useUnmount.ts
In our case you can provide two args to hook: cb and args
Maybe you could store the callback directly into the ref without
useCallback()
: