I'm trying to create a cleanup function for my useEffect
and update the variable cancelRequest
that is being used by the code from my getAllTweets()
function.
let cancelRequest = false;
useEffect(() => {
if (!url) {
dispatch({ type: "IDLE" });
return;
}
getAllTweets();
return function cleanup() {
cancelRequest = true;
};
}, [state.page]);
...
function getAllTweets() {
axiosConfig
.get(`${url}?page=${state.page}`)
.then((response) => {
if (cancelRequest) {
dispatch({ type: "IDLE" });
return;
} else if (state.page !== -1) {
state.page === 1
? dispatch({ type: "LOADED", payload: response.data.data })
: dispatch({
type: "LOADED",
payload: [...state.tweets, ...response.data.data],
});
}
...
})
...
}
But I think the variable wasn't being updated as I'm still getting the Can't perform a React state update on an unmounted component
warning on my app when I'm trying to exit while it was fetching data.
CodePudding user response:
useReducer
performs the same sort of role as useState
- it stores state but is said to be useful for handling complex state logic. But still, if you use useReducer
in a component, and that component becomes unmounted, you shouldn't call dispatch again, because it corresponds to state in that component only. Even just doing
dispatch({ type: "IDLE" });
will produce a React warning.
But there's another potential issue. cancelRequest
is a component-scoped variable, which means there will be a separate binding for that variable each render - so reassigning cancelRequest
in only a single render may not be enough in case there's also an async action going on from a prior render. Use a ref instead, which is persistent across all renders. The cleanup to change the ref should be in a separate useEffect(.., [])
so that it'll run only when the component unmounts, rather than every time the page changes.
const cancelReqestRef = useRef();
useEffect(() => {
// When unmounting, and only when unmounting, change the ref
return () => cancelReqestRef.current = true;
}, []);
useEffect(() => {
if (!url) {
dispatch({ type: "IDLE" });
return;
}
getAllTweets();
}, [state.page]);
...
function getAllTweets() {
axiosConfig
.get(`${url}?page=${state.page}`)
.then((response) => {
if (cancelReqestRef.current) {
// DO NOT update state here with dispatch, just return
return;
} else if (state.page !== -1) {
state.page === 1
? dispatch({ type: "LOADED", payload: response.data.data })
: dispatch({
type: "LOADED",
payload: [...state.tweets, ...response.data.data],
});
}
...
})
...
}