Last active
December 7, 2020 17:49
-
-
Save AlpacaGoesCrazy/25e3a15fcd4e57fb8ccd408d488554d7 to your computer and use it in GitHub Desktop.
Hook for react state which prevents updates on unmounted components
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect, useRef, useState } from 'react' | |
/* | |
If you attempt to set some state after asynchronous request it may happen that component you wish to set state on has been unmounted. | |
This will trigger "Warning: Can’t call setState (or forceUpdate) on an unmounted component." warning. | |
This hooks is `useState` hook which prevents setting state on an unmounted component | |
Usage: | |
const [myState, mySafeSetState] = useSafeState(initialValue) | |
*/ | |
const useSafeState = (initialValue) => { | |
const _isMounted = useRef() // useRef to memorize if the component is mounted between renders | |
const [state, setState] = useState(initialValue) | |
useEffect(() => { | |
_isMounted.current = true | |
return () => { | |
_isMounted.current = false | |
} | |
}) | |
const safeSetState = (...args) => { | |
if(_isMounted.current) { // do not call setState if the component already unmounted | |
setState(...args) | |
} | |
} | |
return [state, safeSetState] | |
} | |
export default useSafeState |
I have found that this does not preserve setState
function identity (in contrast to React), which led to suprising bugs when replacing useState
with useSafeState
. I have therefore modified the implementation to:
export const useSafeState = (initialValue) => {
const _isMounted = useRef();
const [state, setState] = useState(initialValue);
const _setState = useRef((...args) => {
if (_isMounted.current) { setState(...args); }
});
useEffect(() => {
_isMounted.current = true;
return () => { _isMounted.current = false; };
});
return [state, _setState.current];
};
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for creating this @AlpacaGoesCrazy! Super useful. I've modified it a bit to work in TypeScript version – type-compatible drop-in replacement for
useState
:https://gist.github.com/troygoode/0702ebabcf3875793feffe9b65da651a