I have a problem with asynchronous states on React
I use context to determine whether the admin is authenticated and has the right to access a page with a state called isAuth
interface UserContext {
setToken: (token:string) => void;
getToken: () => string|null;
logout: () => void;
setIsAuth: (value:boolean) => void;
isAuth: boolean;
}
const initialState: UserContext = {
setToken: () => {},
getToken: () => null,
logout: () => {},
setIsAuth: () => {},
isAuth: false,
}
const UserContext = createContext<UserContext>(initialState)
export function UserContextProvider ({children}:{children:React.ReactNode}){
const [isAuth, setIsAuth] = useState(false);
console.log("isAuth in UserContextProvider",isAuth);
const setToken = (token:string) => {
window.localStorage.setItem('token', token);
}
const getToken = () => {
return window.localStorage.getItem('token');
}
const logout = () => {
//redirect("/login")
window.localStorage.removeItem('token');
}
return <UserContext.Provider value={{setToken, getToken, logout, setIsAuth, isAuth }}>
{children}
</UserContext.Provider>
}
export const useUserContext = () => {
return useContext(UserContext)
}
main.ts
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<UserContextProvider>
<App />
</UserContextProvider>
</React.StrictMode>,
)
In App, I make an API call to check if the admin is authenticated, and I set isAuth to response.code === 200 (if authenticated, the response is 200). In this case, the admin is authenticated so the response is 200 and isAuth is set to true
function App() {
const {setIsAuth,getToken} = useUserContext();
useEffect(() => {
const token = getToken();
fetch(import.meta.env.VITE_API_URL+"/isAuth", {
headers: {Authorization: `Bearer ${token}`}
})
.then((res) => res.json())
.then((response) => {
console.log("response App",response);
setIsAuth(response.code === 200);
})
}, [location.pathname])
In the ProjectTable component (the admin page that displays projects), I need to check if he is authenticated, and if not, redirect to /login
I made a log of isAuth
export function ProjectTable({projects}:{projects:Project[]}){
const { t } = useTranslation();
const {isAuth} = useUserContext();
const navigateTo = useNavigate();
useEffect(() => {
console.log("isAuth in useEffect ProjectTable",isAuth);
// if (!isAuth){
// navigateTo("/login");
// }
}, [])
Here is the console output when I am on the admin page ProjectTable, which displays the projects
isAuth in UserContextProvider is false and isAuth in ProjectTable is false.
Then I make the API call and set isAuth to true.
isAuth in UserContextProvider is set to true, but not in ProjectTable. I don’t see the log indicating that isAuth is true; I only see the old log showing it as false before isAuth in UserContextProvider becomes true.
So, if I uncomment if (!isAuth), I am redirected to /login even though I shouldn’t be because I am authenticated
2
Answers
With isAuth printed in the function component :
If I uncomment
I am redirected to /login because at the beginning isAuth is false
With isAuth in the useEffect dependency :
If I uncomment
I am redirected to /login because at the beginning isAuth is false
You should print
isAuth
in the function component. Otherwise, the value ofisAuth
is the stale value when the component mounted.Or, add it to the dependency list of
useEffect
hook