Search code examples
reactjsnext.jsvercelswr

useSWR mutate(undefined) doesn't revalidate other windows


I use uswSWR to handle user authentication:

export function useAuthenticatedUser() {
    const { data, error, isLoading, mutate } = useSWR("user",
        UserApi.getAuthenticatedUser,
        {
            shouldRetryOnError(err) {
                return !(err instanceof UnauthorizedError)
            },
        }
    );
    return {
        user: data,
        userLoading: isLoading,
        userLoadingError: error,
        mutateUser: mutate,
    }
}

Because of SWR's automatic revalidation, this user is fetched again when a browser window or tab gains focus.

Problem: Revalidation doesn't work after logging out:

const { mutateUser } = useAuthenticatedUser();
async function logout() {
    try {
        await UserApi.logout();
        mutateUser(undefined);
    } catch (error) {
        console.error(error);
        alert(error);
    }
}

This recording shows the problem in action. Note that the window gaining focus revalidates after logging in but not after logging out.

enter image description here


Solution

  • The problem was that I didn't return null if the user was not logged in. Logout propagates properly if I mutate with null instead of undefined.

    export function useAuthenticatedUser() {
        const { data, error, isLoading, mutate } = useSWR("user",
            async () => {
                try {
                    return await UsersApi.getAuthenticatedUser();
                } catch (error) {
                    if (error instanceof UnauthorizedError) {
                        return null;
                    } else {
                        throw error;
                    }
                }
            }
        );
    
        return {
            user: data,
            userLoading: isLoading,
            userLoadingError: error,
            mutateUser: mutate,
        }
    }
    

    Now mutate accepts null:

    const { mutateUser } = useAuthenticatedUser();
    async function logout() {
        try {
            await UserApi.logout();
            mutateUser(null); // undefined won't propagate between tabs
        } catch (error) {
            console.error(error);
            alert(error);
        }
    }