Title rewritten to: Protected routes in React Route v6 not updating when user logs in
<p>I'm trying to log in a user. I have a login component which receives the credentials and then I use the redux toolkit to store the state and the validation and everything is done in userSlice. I have a protected route that should check if the user is logged in and if the user is not logged in it should not navigate to the recipe page I have. When I try to use the useSelecter hook to access the user from a protected routing component, it returns null on the first render, but does return the user on the second render, but the login still fails. In redux dev tools, the status is updated nicely. Is there a way to get the user object on the first render of the protected routing component? (As you can see I'm using useEffect hook and have dependencies array). </p>
<p>Thank you very much for your help.
Thanks. </p>
<p>Here is my code: </p>
<p>Login.js -- This file is responsible for receiving the credentials, then dispatching an action using useDispatch and updating the state using useDispatch. </p>
<pre class="brush:php;toolbar:false;">import React, { useState } from 'react';
import { loginUser } from '../../features/users/userSlice';
import { useSelector, useDispatch } from 'react-redux'
import { useNavigate } from 'react-router-dom';
export default function Login() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const user = useSelector(state => state.user.user)
const dispatch = useDispatch()
const navigate = useNavigate()
return (
<div>
<input
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit" onClick={() => {
dispatch(loginUser({email, password}))
navigate('/recipes')
}}>submit</button>
</div>
)
}</pre>
<p>ProtectedRoute.js -- This component ensures that if the user is not authenticated, he will not be able to log in</p>
<pre class="brush:php;toolbar:false;">import React, { useState, useEffect } from "react";
import { Route, Navigate, Outlet } from "react-router-dom";
import { useSelector } from 'react-redux';
export default function ProtectedRoute({ children }) {
const [ activeUser, setActiveUser ] = useState(false)
const user = useSelector((state) => state.user);
useEffect(() => {
if (!user.isLoading) {
user.success ? setActiveUser(true) : setActiveUser(false)
console.log('active user: ' activeUser)
}
}, [user])
return (
activeUser ? <Outlet /> : <Navigate to="/login"/>
)
}</pre>
<p>app.js -- This component contains all routes, including protected routes. </p>
<pre class="brush:php;toolbar:false;">import React from "react";
import Recipes from "./components/recipes/recipes";
import Login from "./components/users/Login";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import ProtectedRoute from "./utils.js/ProtectedRoute";
const App = () => {
return (
<div>
<BrowserRouter>
<Routes>
<Route path="/login" index element={<Login />} />
<Route path="/" element={<Navigate replace to="/login" />}/>
<Route element={<ProtectedRoute />}>
<Route element={<Recipes/>} path="/recipes" />
</Route>
</Routes>
</BrowserRouter>
</div>
);
};
export default App;</pre>
<p>userSlice.js -- Since I use the redux toolkit, I have slices with different functions. There are reducers here, which have user status. </p>
<pre class="brush:php;toolbar:false;">import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
const loginUrl = 'http://localhost:5000/api/login';
const signupUrl = 'http://localhost:5000/api/signup';
export const loginUser = createAsyncThunk('user/loginUser', async (data) => {
const response = await axios.post(loginUrl, data);
return response;
})
export const signupUser = createAsyncThunk('user/signupUser', async (data) => {
const response = await axios.post(signupUrl, data);
return response;
})
const initialState = {
user: {},
isLoading: true
}
const userSlice = createSlice({
name: 'user',
initialState,
reducers: {
getPassword: (state, action) => {
const password = action.payload
console.log(password)
}
},
extraReducers: {
[loginUser.pending]: (state) => {
state.isLoading = false
},
[loginUser.fulfilled]: (state, action) => {
state.isLoading = false
state.user = action.payload.data
},
[loginUser.rejected]: (state) => {
state.isLoading = false
},
[signupUser.pending]: (state) => {
state.isLoading = false
},
[signupUser.fulfilled]: (state, action) => {
state.isLoading = false
state.user = action.payload.data
},
[signupUser.rejected]: (state) => {
state.isLoading = false
},
}
})
export const { getPassword } = userSlice.actions
export default userSlice.reducer;</pre></p>