How come my AuthContext unmounts when isAuthenticated changes its value?

Viewed 12

Currently my code works as I want it to work, however, I don't quite understand why it is working. I have a React context in which I store my authentication related logic. You can see that inside the useEffect, if the user is authenticated, I register a listener, and on dismount I unregister the listener and console.log('Unmounted');. This works fine and dandy, however, I don't quite understand why would the whole AuthContext unmount when I log out. Is there a scenario where the user might log out without triggering the function which unregisters the listener?

export const AuthContext = createContext<AuthContextType>(defaultAuthContext);
export const useAuth = () => useContext(AuthContext);

export const AuthProvider: FunctionComponent = ({ children }) => {
    const [jwtToken, setJwtToken] = useState('');
    const [idpToken, setIdpToken] = useState('');
    const [user, setUser] = useState<User>();
    const [isAuthenticated, setIsAuthenticated] = useState(false);
    const [isLoadingToken, setIsLoadingToken] = useState(true);
    const [isLoadingUser, setIsLoadingUser] = useState(false);
    const navigation = useNavigation<LandingScreenNavigationProp>();

    const saveJwtToken = useCallback((token: string) => {
        setJwtToken(token);
        attachJwtToken(token);
    }, []);

    const fetchUser = useCallback(async () => {
        try {
            setIsLoadingUser(true);
            const self = await getSelf();
            setUser(self.user);
            analytics.identifyUser(self);
            Logger.attachUserInfo(self.user.id.toString(), self.user.email ?? '');
        } finally {
            setIsLoadingUser(false);
        }
    }, []);

    const login = useCallback(
        async (email: string, password: string, otpAttempt?: string) => {
            setIsLoadingToken(true);
            try {
                const { jwtToken: jwt, idpToken: idp } = await authenticate({ email, password, otpAttempt });
                saveJwtToken(jwt);
                setIdpToken(idp);
                await fetchUser();
                await registerFirebaseToken();

                analytics.track(event.loginSuccess);

                setIsAuthenticated(true);
            } catch (error) {
                if (error.message === 'general.errors.general') Logger.error(error, 'Failed to load user information');
                throw new Error(error.message);
            } finally {
                setIsLoadingToken(false);
            }
        },
        [fetchUser, saveJwtToken]
    );

    const onWebViewLogin = useCallback(
        async (jwt: string, idp: string) => {
            setIsLoadingToken(true);
            try {
                saveJwtToken(jwt);
                setIdpToken(idp);
                await fetchUser();
                await registerFirebaseToken();

                storeTokens({ jwtToken: jwt, idpToken: idp });
                setIsAuthenticated(true);
            } catch (error) {
                Logger.error(error, 'Failed to load user information');
            } finally {
                setIsLoadingToken(false);
            }
        },
        [fetchUser, saveJwtToken]
    );

    const logout = useCallback(async () => {
        detachJwtToken();
        setIsAuthenticated(false);
        setJwtToken('');
        setIdpToken('');
        setUser(undefined);

        queryClient.invalidateQueries();

        clearTokens();
        analytics.reset(await getSelf());
    }, []);

    const onSessionExpiration = useCallback(() => {
        logout();
        navigation.navigate('EnterEmail', { message: translate('general.errors.timed_out') });
    }, [logout, navigation]);

    useDidMount(() => {
        (async () => {
            try {
                setIsLoadingToken(true);
                const idp = await getIDP();
                const jwt = await getJWT();

                if (idp) setIdpToken(idp);
                if (!jwt) return;

                saveJwtToken(jwt);

                await registerFirebaseToken();
                await fetchUser();

                setIsAuthenticated(true);
            } catch {
                setIsAuthenticated(false);
            } finally {
                setIsLoadingToken(false);
            }
        })();
    });

    useEffect(() => {
        if (!isAuthenticated) return;

        const guard = Doorman.guard(onSessionExpiration);
        Doorman.refreshTokenListener((token: string) => {
            storeJWT(token);
            saveJwtToken(token);
        });

        return () => {
            console.log('Unmounted');
            guard.remove();
        };
    }, [isAuthenticated, onSessionExpiration, saveJwtToken]);

    const values = {
        isAuthenticated,
        isLoading: isLoadingToken || isLoadingUser,
        idpToken,
        jwtToken,
        user,
        login,
        onWebViewLogin,
        logout,
        onSessionExpiration,
    };

    return <AuthContext.Provider value={values}>{children}</AuthContext.Provider>;
};
0 Answers
Related