I have a simple app where there is a login page and upon submitting the form, it makes a request to the backend which returns a token upon successful authentication, which then redirects to internal dashboard page. There is also another page called settings and another is the NotFound (404) page.
After logging in, when I'm in dashboard page or settings page, if I refresh, it takes me to 404 page whereas it should stay at the page I'm in. Does anybody know what the issue could be?
Here's the code:
App.js
const App = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const handleLogin = (token) => {
localStorage.setItem('token', token);
setIsAuthenticated(true);
};
useEffect(() => {
const token = localStorage.getItem('token');
setIsAuthenticated(!!token);
}, []);
return (
<Router>
{
isAuthenticated ? (
<Routes>
<Route path="/dashboard" element={
<Layout>
<Dashboard />
</Layout>
} />
<Route path="/settings" element={
<Layout>
<Settings />
</Layout>
} />
<Route path="*" element={<Layout><NotFound /></Layout>} />
</Routes>
) : (
<Routes>
<Route path="/" element={<Login onLogin={handleLogin} />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
)
}
</Router>
);
}
Login.js
const Login = ({ onLogin }) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
const response = await fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
});
if (response.ok) {
const result = await response.json();
if (result.success) {
localStorage.setItem('token', result.token);
onLogin(result.token);
navigate('/dashboard');
}
}
};
return (
<div class="container">
<div class="formContainer">
<h1>LOGIN</h1>
<form onSubmit={handleSubmit}>
<input
type="text"
value={username}
placeholder="username"
onChange={(e) => setUsername(e.target.value)}
required
/>
<input
type="password"
value={password}
placeholder="password"
onChange={(e) => setPassword(e.target.value)}
required
/>
<button type="submit" className="login-button">
Login
</button>
</form>
</div>
</div>
);
}
The setState or for your case, setIsAuthenticated
from useState
is asynchronously set in React. So, initially your isAuthenticated
is false
, it doesn't wait for your setIsAuthenticated
.
Possible fixes:
null
.const App = () => {
const [isAuthenticated, setIsAuthenticated] = useState(null);
const handleLogin = (token) => {
localStorage.setItem('token', token);
setIsAuthenticated(true);
};
useEffect(() => {
if (isAuthenticated !== null) return;
const token = localStorage.getItem('token');
setIsAuthenticated(!!token);
}, []);
...
So, in this case, isAuthenticated is not false or defined, so it will then proceed to check the login status, if it's false, then automatically goes to login page.
const [isAuthenticated, setIsAuthenticated] = useState(
!!localStorage.getItem('token') // Initialize from localStorage
);
In this case, the token is fetched directly from the localstorage on initial load.