This is my code to happen when submit button is clicked
const location = useLocation();
const from = location.state?.from?.pathname || "/";
// The above lines are perfectly fine in the parent function
const handleSubmit = async (e) => {
e.preventDefault();
loginDetails.userName = user;
loginDetails.password = pwd;
try {
const response = await axios.post(
LOGIN_URL,
JSON.stringify({ user, pwd }),
{
headers: { 'Content-Type': 'application/json' },
withCredentials: true,
}
);
const accessToken = response?.data?.accessToken;
const roles = response?.data?.roles;
setAuth({ user, pwd, roles, accessToken });
setUser('');
setPwd('');
navigate(from, {replace:true});
} catch (err) {
if (!err?.response) {
console.log(err);
setErrMsg('No Server Response');
} else if (err.response?.status === 400) {
setErrMsg('Missing Username or Password');
} else if (err.response?.status === 401) {
setErrMsg('Unauthorized');
} else {
setErrMsg('Login Failed');
}
errRef.current.focus();
}
};
This is my Router.js responsible for routing the pages
const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path='/' element={<Login />}>
{/* Public route */}
<Route path='login' element={<Login />} />
{/* Private routes */}
{/* The below is for the user */}
<Route path='grievance/*' element={<RequireAuth allowedRoles={[ROLES.User]} />}>
<Route element={<Grievance />} />
</Route>
{/* The below is for the admin */}
<Route path='admin/*' element={<RequireAuth allowedRoles={[ROLES.Admin]} />}>
<Route element={<Admin />} />
</Route>
{/* catch everything else */}
<Route path='*' element={<Missing />} />
</Route>
</Routes>
</BrowserRouter>
);
};
This is my RequireAuth.js
responsible for checking authorization of the person
const RequireAuth = ({ allowedRoles }) => {
const { auth } = useAuth();
const navigate = useNavigate();
console.log("Here in require auth");
if (auth?.roles?.find(role => allowedRoles?.includes(role))) {
console.log("for outlet");
return <Outlet />;
} else if (auth?.user) {
console.log("for unauthorized");
navigate("/unauthorized", { replace: true });
return null;
} else {
console.log("Authlogin");
navigate("/login", { replace: true });
return null;
}
};
And the console has the log of only the "navigating to grievance page" and nothing else from RequireAuth.js
, and in backend of my code everything is running fine and in the frontend I am responded with all the data correctly. But I am unable to navigate from one page to the other Please help me in this
Grievance
is not rendering though it is navigating to "/grievance"
. I just checked that.
If you are asking for my useAuth(). the code is below
import { createContext, useState } from "react";
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [auth, setAuth] = useState({});
// console.log("auth=",auth);
return (
<AuthContext.Provider value={{ auth, setAuth }}>
{children}
</AuthContext.Provider>
)
}
export default AuthContext;
Even after going through the post suggested by the comment, I still could not figure out what went wrong
I suspect the issue here is that you are rendering Login
as a layout route component on the root "/"
parent route and that Login
doesn't render an Outlet
component for the nested routes to render out their element
content. This appears to agree with the behavior you describe where the URL path changes to "/grievance"
but the Login
component is still rendered and the nested route isn't reached and so you get none of the logged output from your RequireAuth
component.
Remove element={<Login />}
from the root route so an Outlet
component is rendered by default. If you don't have any specific "home page" content to display on path "/"
then you can remove this route entirely and let the path="*"
handle it.
Example:
const Router = () => {
return (
<BrowserRouter>
<Routes>
{/* Public route */}
<Route path='login' element={<Login />} />
{/* Private routes */}
{/* The below is for the user */}
<Route element={<RequireAuth allowedRoles={[ROLES.User]} />}>
<Route path='grievance' element={<Grievance />} />
</Route>
{/* The below is for the admin */}
<Route element={<RequireAuth allowedRoles={[ROLES.Admin]} />}>
<Route path='admin' element={<Admin />} />
</Route>
{/* catch everything else */}
<Route path='*' element={<Missing />} />
</Routes>
</BrowserRouter>
);
};
The RequireAuth
component should render the Navigate
component instead of using the navigate
function in the render return directly. It should also forward the current location
so the Login
component has a from
value to use from route state.
const RequireAuth = ({ allowedRoles = [] }) => {
const location = useLocation();
const { auth } = useAuth();
if (auth?.roles?.some(role => allowedRoles.includes(role))) {
return <Outlet />;
} else if (auth?.user) {
return <Navigate to="/unauthorized" replace />;
} else {
return <Navigate to="/login" replace state={{ from: location }} />;
}
};