I have created a custom hook to fetch data. In that hook I have an async function looking something like this:
const useData = () => {
const [enabled, setEnabled] = useState(true);
const [data, setData] = useState(false);
const fetchData = async () => {
const response = await fetch();
const data = await response.json();
while (data.continue && enabled) {
response = await fetch(data.requestId);
data = await response.json();
}
};
useEffect(() => {
console.log(enabled);
}, [enabled]);
return { data, setEnabled };
};
When I call setEnabled(false) from a component it's set to false when the useEffect logs it but it continues to be true in the fetchData function and it never cancels the fetch which I was expecting.
CodePudding user response:
This is because the fetchData
callback function "captures" the state of the data
variable when it is first called, and it doesn't know about the new value of data
when you change it (this is known in React as stale closures). To fix this, you have to use the useRef
hook instead:
const useData = () => {
const [enabled, setEnabled] = useState(true);
const [data, setData] = useState(false);
const isCancelled = useRef(false);
const fetchData = async () => {
const response = await fetch();
const data = await response.json();
while (data.continue && !isCancelled.current) {
response = await fetch(data.requestId);
data = await response.json();
}
};
useEffect(() => {
isCancelled.current = !enabled;
}, [enabled]);
return { data, setEnabled };
};