I'm using React Router v6 and am creating private routes for my application. i must to close all routes except login until user is authenticated
this is my private route
import React from 'react';
import { Navigate } from 'react-router-dom';
import loginAction from '@/api/AuthProvider';
export function PrivateRoute({ children }) {
const { user } = loginAction();
return user ? children : <Navigate to="/login" />;
}
this is my request
export default function loginAction() {
return new Promise((resolve) => {
resolve({
user: {
fullName: 'Elon Mask',
dob: '2022-07-27T12:46:26.356Z',
email: '[email protected]',
defaultCurrency: 'USD'
},
token: 'DTYHKL57HGGJ'
});
}).then((data) => {
localStorage.setItem('token', data.token);
return data;
});
}
This is my App
export const App = () => {
return (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<Routes>
<Route path="/login" element={<Login />} />
<Route
element={
<PrivateRoute>
<AppLayout />
</PrivateRoute>
}
>
<Route
path="/"
element={
<PrivateRoute>
<Landing />
</PrivateRoute>
}
/>
<Route
path="/about"
element={
<PrivateRoute>
<About />
</PrivateRoute>
}
/>
<Route
path="/categories"
element={
<PrivateRoute>
<Categories />
</PrivateRoute>
}
/>
<Route
path="*"
element={
<PrivateRoute>
<Fallback />
</PrivateRoute>
}
/>
<Route
path="/expenses"
element={
<PrivateRoute>
<Expenses />
</PrivateRoute>
}
/>
</Route>
</Routes>
</BrowserRouter>
</QueryClientProvider>
);
};
but when i submit, it does not redirect me to the home page what i am doing wrong? Is there something I'm missing?
CodePudding user response:
You're trying to use loginAction
as a react hook when in reality it's an asynchronous function that returns a promise.
The way that I would suggest solving this is converting the loginAction
action into a react hook. Read more about hooks here: https://reactjs.org/docs/hooks-intro.html
Essentially the two main parts of a hook are that:
- The name starts with the word
use
such asuseLoginAction
- It can create/return state variables and when those state variables changes, components that use the hook will also re-render.
So the first change is wrapping loginAction into a hook:
If you don't understand how the useEffect
hook works, look at this: https://reactjs.org/docs/hooks-effect.html
import { useState, useEffect } from "react";
function loginAction() {
return new Promise((resolve) => {
resolve({
user: {
fullName: 'Elon Mask',
dob: '2022-07-27T12:46:26.356Z',
email: '[email protected]',
defaultCurrency: 'USD'
},
token: 'DTYHKL57HGGJ'
});
}).then((data) => {
localStorage.setItem('token', data.token);
return data;
});
}
export default function useLoginAction(){
const [data, setData] = useState({ user: null });
useEffect(() => {
loginAction().then((response) => setData(response));
}, []);
return data;
}
After that you can use the hook in your private route function. There are less repetitive ways to conditionally render the routes but that's outside the scope of this question:
import React from 'react';
import { Navigate } from 'react-router-dom';
import useLoginAction from '@/api/AuthProvider';
export function PrivateRoute({ children }) {
const { user } = useLoginAction();
return user ? children : <Navigate to="/login" />;
}
And this should work the way you're expecting it to work from my understanding.
CodePudding user response:
You can control access to routes by some flag (isAuth) or other value. I'm doing this:
const App = () => {
const navigate = useNavigate()
const accessToken = useAppSelector((state) => state.authSlice.accessToken)
useEffect(() => {
if (accessToken === '') {
navigate('/login', { replace: true })
} else {
navigate('/', { replace: true })
}
}, [accessToken])
return (
<>
{accessToken !== '' ? (
<Routes>
<Route path='/' element={<Main />} />
<Route path='/profile' element={<Profile />} />
<Route path='*' element={<NotFound />} />
</Routes>
) : (
<Routes>
<Route path='/login' element={<Login />} />
<Route path='/registration' element={<Registration />} />
<Route path='*' element={<NotFound />} />
</Routes>
)}
<StatusBar />
</>
)
}
export default App