Sometimes I need to run a side effect in React when a certain value in the state changes.
I do this quite often and found that for me the most readable (and understandable) method is to use a hook that stores the previous value of something.
export function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
Then when I want to create a side effect that only runs when when a specific variable changes:
const [step, setStep] = useState(1);
const [something, setSomething] = useState('Hi world!');
const prevStep = usePrevious(step);
useEffect(() => {
if (prevStep !== 4 && step === 4) {
// Do something
}
}, [step, prevStep, something]);
Does anyone else do it like this way? How often do you need this pattern?
Example
This is one example that I found in a Upload component where I wanted to implement the upload directly in the component. This can be done in a much cleaner way with eg. Redux-Saga
There is a callback onUploaded
that should be called when the upload is done.
useEffect(() => {
if (progress === null) {
setProgress(0); // This marks the upload as started
uploadBlob(blob, setProgress).then(() => {
setBlobUploaded(true);
onUploaded(); // Dangerous: This might be out of date by the time it's called
}).catch(reason => {
setUploadFailedReason(reason);
});
}
}, [blob, progress, onUploaded]);
useEffect(() => {
if (progress === null) {
setProgress(0); // This marks the upload as started
uploadBlob(blob, setProgress).then(() => {
setBlobUploaded(true);
}).catch(reason => {
setUploadFailedReason(reason);
});
}
}, [blob, progress]);
useEffect(() => {
if (prevIsBlobUploaded === false && isBlobUploaded === true) {
onUploaded(); // This is safer, but it's not clear what the state of the blob is
}
}, [isBlobUploaded, prevIsBlobUploaded, onBlobUploaded]);
Top comments (0)