So I have this app that need to refresh its JWT token when coming from the background. I'm not sure if this is the best way to handle token refresh but I want this to work so I can improve the mechanism later on. My refresh token lives for 365 days and my access token lives for 12 hours.
import React, {useEffect, useState} from "react";
import {AppState, Platform} from "react-native";
import SplashScreen from "react-native-splash-screen";
import {AppRouter} from "./AppRouter";
import {addBackgroundHandler} from "./services/RoutingHelperService";
import {JwtToken} from "./types/types";
import {getTokenFromDevice} from "./services/StorageService";
import {refreshJwtToken} from "./services/AuthService";
import useDidMountEffect from "./useDidMountEffect";
export function App() {
const [jwtToken, setJwtToken] = useState<JwtToken | null>(null);
const [appInitiated, setAppInitiated] = useState(false);
useEffect(() => {
appStartup().then(() => console.log("Successfully started app"));
AppState.addEventListener("change", refreshTokenFromBackground);
// Platform specific handlers
if (Platform.OS === "android") {
addBackgroundHandler();
}
async function appStartup() {
const token = await getTokenFromDevice();
if (token) {
setJwtToken(token);
}
SplashScreen.hide();
}
}, []);
useDidMountEffect(() => {
if (!appInitiated) {
setAppInitiated(true);
}
}, [jwtToken]);
async function refreshTokenFromBackground() {
if (AppState.currentState === "active" && jwtToken) {
setJwtToken(await refreshJwtToken(jwtToken.refresh_token));
}
}
return <AppRouter />;
}
So every first time the app boots, I add the appstate event listener (Which is called "change") that listens to the app going to the background or the app going to the foreground (state is called 'active' in this case)
Now when I come from the background, the app successfully gets a new JWT token. But when I go to the background again, its send the non-updated refresh token. Now, I know that state needs to time update, but here is what I find strange: even if I wait half a minute before going to the background again, it still sends the old refresh token. I can see the token change when I put it in a component.
Does anyone know what I'm doing wrong? Any advice is highly appreciated :)
BTW: This is the component useDidMomentEffect which is basicly useEffect which only listens to the an value changing instead of on mount and on change.
import { useEffect, useRef } from 'react';
const useDidMountEffect = (func, deps) => {
const didMount = useRef(false);
useEffect(() => {
if (didMount.current) func();
else didMount.current = true;
}, deps);
};
export default useDidMountEffect;
Update 1:
I actually found out that adding this eventhandler where it is now, resulting in jwt token to be always null. I can do calls with a valid jwt token but inside the refresh function, the token is null. So my problem now actually changed, now I wonder why this function can't access the state of my app.
Update 2 - Answer
So I discovered that useState inside the listener is not updated with its state, the solution is to use useRef. Now I can go to the background and to the foreground 10 times in 15 seconds and I never get a invalid refresh token. I found the answer here because i suspected the listener of being the problem: Wrong React hooks behaviour with event listener