简体   繁体   中英

Warning: Can't perform a React state update on an unmounted component after update context and redirect to another page

I am having a problem which needs your helps

I have a changeProfile function which will be executed after onClick in /editpage

const appContext = useContext(AppContext);
const [userLogin, setUserLogin] = appContext.userLogin;
const [userProfile, setUserProfile] = useState<UserProfile>({
    name: userLogin.name,
    ....
});
const changeProfile = e => {
    e.preventDefault();
    post(API)
        .then(() => {
            setUserLogin({
                ...userLogin,
                name: userProfile.name,
            });
            router.push('/mypage');
        })
        .catch(error => {
            setEditError(error[0]);
            window.scrollTo(0, 0);
        });
};

context.tsx

const [userLogin, setUserLogin] = useState({
     email: '',
     name: '',
     ....
});

after page redirects to /mypage , console logs warning error as title and it only logs warning at the first time (if I back to /editpage and changeProfile one more time, console logs nothing in /mypage )

I have tried to redirect after setUserLogin done as code below but it's not working

const changeProfile = e => {
    e.preventDefault();
    post(API)
        .then(() => {
            setUserLogin({
                ...userLogin,
                name: userProfile.name,
            });
        })
        .catch(error => {
            setEditError(error[0]);
            window.scrollTo(0, 0);
        });
};

const firstUpdate = useRef(true);
useLayoutEffect(() => {
    if (firstUpdate.current) {
        firstUpdate.current = false;

        return;
    }
    console.log(userLogin); //already updated
    router.push('/mypage');
}, [userLogin]);

Thanks for reading!

PS: Problem can be solved with

setTimeout(() => {
    router.push('/mypage');
}, 1000);

but its absolutely not a right choice

As the warning suggests, because your API code is asynchronous, if you redirect away from the page too fast, the setState may occur after the component has already been unmounted. What you can do, is use the callback argument of setState (assuming you're calling setState somewhere in the setUserLogin function), to redirect after the state update has already been completed.

Edit : You could try adding another state variable to your context to signify that an update has been performed, something like updated: false and then with your setUserLogin call:

setUserLogin({
    ...userLogin,
    name: userProfile.name,
    updated: true
});

Then you can use the useEffect hook to check for this condition and then redirect:

useEffect(() => {
    if (userLogin.updated) {
        router.push('/mypage');
    }
})

Then at some point, you would have to reset this variable to false . However, now that I know your setUserLogin is coming from the AppContext component, I think the issue is that this component is getting unmounted when it shouldn't be. Make sure you're rendering this component high enough in the component tree so that the mypage you redirect to still has this AppContext mounted

Edit 2 : A good approach to prevent this error is by adding a guard to a component class, which keeps track of whether the component is mounted or not. Something like this:

class AppContext extends Component {
    _mounted = False;

    componentDidMount() {
        this._mounted = true;
    }

    componentWillUnmount() {
        this._mounted = false;
    }

    ...
}

Then, when you call setState or the update function from useState , you can first check to see if the component is mounted or not before trying to update:

if (AppContext._mounted) {
    setUserLogin({
        ...userLogin,
        name: userProfile.name,
        updated: true
    });
}

However, in your case, this may prevent the information from being updated as you expect, since the component is unmounting, which is why I think the issue lies with why the component is unmounting

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM