What would be the best practice for private routing? Maybe I'm doing something wrong, but when user logged in I already am redirect to the /login page
And my second question: Which of these versions is better or you have even better idea?
Code:
Auth
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: false,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isUserLoggedIn = false
})
},
})
export const me = createAsyncThunk('auth/me', async () => {
try {
const user = await userService.getUserData()
return { userData: user.data.data }
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString()
return message
}
})
CASE 1:
App
function App() {
const dispatch = useDispatch()
useEffect(() => {
dispatch(me())
}, [])
const auth = useSelector((state) => state.auth)
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes isAuthenticated={auth.isUserLoggedIn} />
</BrowserRouter>
</div>
)
}
export default App
Routes
export const AppRoutes = ({ isAuthenticated }) => (
<Routes>
<Route
path='/login'
element={<Login />}
/>
<Route
path='/dashboard'
element={
<PrivateRoute isAuthenticated={isAuthenticated}>
<Stats />
</PrivateRoute>
}
/>
...
PrivateRoute
export const PrivateRoute = ({ children, isAuthenticated }) => {
return isAuthenticated ? children : <Navigate to='/login' />
}
CASE 2:
App
function App() {
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</div>
)
}
export default App
Routes
export const AppRoutes = () => (
<Routes>
<Route
path='/login'
element={<Login />}
/>
<Route
path='/dashboard'
element={
<PrivateRoute>
<Stats />
</PrivateRoute>
}
/>
...
PrivateRoute
export const PrivateRoute = ({ children }) => {
const dispatch = useDispatch()
const { isUserLoggedIn } = useSelector((state) => state.auth)
useEffect(() => {
if (isUserLoggedIn === null) {
dispatch(me())
}
}, [])
return isUserLoggedIn ? children : <Navigate to='/login' />
}
CASE 1 or CASE 2 is better to approach or maybe you have a better idea?
For these 2 ideas, it redirects very quickly to /login when I go to /dashboard
What I want to achieve is good practice, quick verification, and waiting until we receive a positive response from the backend that the user is authenticated
Team, what do you suggest?
EDIT
New version:
Redux
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: true,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isLoading = false
state.isUserLoggedIn = false
})
},
})
export const me = createAsyncThunk('auth/me', async ({}, thunkAPI) => {
try {
const user = await userService.getUserData()
return { userData: user.data.data }
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString()
thunkAPI.rejectWithValue(message)
return message
}
})
PrivateRoute
export const PrivateRoute = ({ children }) => {
const dispatch = useDispatch()
const { isLoading, isUserLoggedIn } = useSelector((state) => state.auth)
if (isLoading) return null
return isUserLoggedIn ? (
children
) : (
<Navigate
to='/login'
replace
/>
)
}
App
function App() {
const dispatch = useDispatch()
const { isUserLoggedIn } = useSelector((state) => state.auth)
useEffect(() => {
if (isUserLoggedIn === null) {
dispatch(me())
}
}, [])
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</div>
)
}
export default App
Store
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
import authReducer from '../features/auth/authSlice'
export const store = configureStore({
reducer: {
auth: authReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ serializableCheck: false }),
})
Index.js
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
But when I reload the page I go to /login don't stay at the same page like /dashboard how could I cope with that?