I'm new to ReactJS and am building a basic application. Here I'm using protected router and implementing an authorization mechanism with useContext and local storage. The aim is to redirect users that are not logged in to the Login Page if they attempt to access Dashboard.
After I do log in, the access token is saved to local storage and account info is saved in auth context. Then I go to Dashboard and I reload the page. I implemented a useEffect hook to check for token in local storage and I thought that when I reload at the Dashboard page, the auth provider would check for the token and return a result that I'm authenticated. However it doesn't work as expected so I am redirected to Login page (Although the useEffect callback was triggered)
Below is my code:
src\components\App\index.js
import { Routes, Route } from 'react-router-dom';
import Login from '../Login';
import Signup from '../Signup';
import GlobalStyles from '../GlobalStyles';
import ThemeProvider from 'react-bootstrap/ThemeProvider';
import RequireAuth from '../RequireAuth';
import Layout from '../Layout';
import Dashboard from '../Dashboard';
import Account from '../Account';
function App() {
return (
<GlobalStyles>
<ThemeProvider>
<Routes>
<Route path="/" element={<Login />} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
<Route element={<RequireAuth />}>
<Route path="/" element={<Layout />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/account" element={<Account />} />
</Route>
</Route>
</Routes>
</ThemeProvider>
</GlobalStyles>
);
}
export default App;
src\components\RequireAuth\index.js
import { useLocation, Navigate, Outlet } from 'react-router-dom';
import useAuth from '../../hooks/useAuth';
function RequireAuth() {
const { auth } = useAuth();
const location = useLocation();
return auth?.user ? (
<Outlet />
) : (
<Navigate to={{ pathname: '/', state: { from: location } }} replace />
);
}
export default RequireAuth;
src\hooks\useAuth.js
import { useContext } from 'react';
import { AuthContext } from '../context/AuthProvider';
function useAuth() {
return useContext(AuthContext);
}
export default useAuth;
src\context\AuthProvider.js
import { useEffect } from 'react';
import { createContext, useState } from 'react';
import api from '../helper/api';
const AuthContext = createContext({});
function AuthProvider({ children }) {
const [auth, setAuth] = useState({});
useEffect(() => {
const apiHelper = new api();
apiHelper.getAccountInfo().then((response) => {
setAuth(response.data);
});
}, []);
console.log(auth.user);
return (
<AuthContext.Provider value={{ auth, setAuth }}>
{children}
</AuthContext.Provider>
);
}
export { AuthContext, AuthProvider };
src\components\Login\index.js
import { useState, useRef, useEffect } from 'react';
import clsx from 'clsx';
import { Form, FormGroup, Button } from 'react-bootstrap';
import Alert from 'react-bootstrap/Alert';
import FloatingLabel from 'react-bootstrap/FloatingLabel';
import { useNavigate } from 'react-router';
import styles from './style.module.scss';
import logo from '../../assets/images/logo.png';
import LoadingSpinner from '../LoadingSpinner';
import api from '../../helper/api';
import useAuth from '../../hooks/useAuth';
const Login = () => {
const usernameRef = useRef();
const errorRef = useRef();
const [state, setState] = useState({
username: '',
password: ''
});
const { username, password } = state;
const [error, setError] = useState();
const [loading, setLoading] = useState(false);
useEffect(() => {
usernameRef.current.focus();
}, []);
const { setAuth } = useAuth();
const navigate = useNavigate();
const submitForm = (event) => {
event.preventDefault();
setLoading(true);
const apiHelper = new api();
apiHelper
.login({
username,
password
})
.then((response) => {
setLoading(false);
setError();
setAuth({ user: response.data.user });
localStorage.setItem('token', response.data.token);
navigate('/dashboard');
})
.catch((error) => {
setLoading(false);
setError(error.response.data.message);
usernameRef.current.focus();
});
};
return (
/** Some hmtml */
);
};
export default Login;
A video on how the error occurs: https://streamable.com/b1cp1t
Can anyone tell me where I'm wrong and how to fix it? Many thanks in advance!