I'm using react-router-dom6 & redux-toolkit & antd form and I'm saving the token to localstorage. what I want is that after logging in, redirecting to dashboard page but instead it redirects to empty page with the same url of dashboard & when I reload the dashboard page contents displayed
routes.tsx
{!token ? (
<Routes>
<Route path="/" element={<Navigate to="/login" />} />
<Route path="/login" element={<Login />} />
<Route path="/signUp" element={<SignUp />} />
<Route path="/resetPassword" element={<ResetPassword />} />
</Routes>
) : (
<>
<Sidebar collapsed={collapsed} />
<Layout className="site-layout">
<Header collapsed={collapsed} collapseHandler={collapseHander} />
<div className="contentContainer">
<Routes>
<Route path="/" element={<Navigate to="/dashboard" />} />
<Route path="/dashboard" element={<Dashboard />} />
{role === "1" && <Route path="/groupList" element={<GroupList />} />}
<Route path="/newOrder" element={<NewOrder />} />
</Routes>
</div>
<Footer>footer</Footer>
</Layout>
</>
)}
loginSlice.ts
export const postLogIn = createAsyncThunk(
"login/postLogIn",
({ email, password }: Login, { dispatch }) => {
return axios
.post("/login", {
email,
password
})
.then((response) => {
successMessage(response.data.status);
if (response.status === 200) {
console.log(response.data.data, "login");
localStorage.setItem("token", response.data.data.Token);
localStorage.setItem("role", response.data.data.role);
localStorage.setItem("user_id", response.data.data.user_id);
localStorage.setItem("user_name", response.data.data.full_name);
localStorage.setItem("email", response.data.data.email);
}
})
.catch((err) => {
errorMessage(err.message);
});
}
);
const logInSlice = createSlice({
name: "login",
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(postLogIn.pending, (state) => {
state.loading = true;
state.error = false;
});
builder.addCase(postLogIn.fulfilled, (state) => {
state.loading = false;
state.error = false;
});
builder.addCase(postLogIn.rejected, (state) => {
state.loading = false;
state.error = true;
});
}
});
export default logInSlice.reducer;
loginForm.tsx
const navigate = useNavigate();
const onFinish = (values: any) => {
dispatch(postLogIn(values));
if (!error) {
navigate("/dashboard", { replace: true });
}
};
<Form
className="form"
name="login-form"
wrapperCol={{
span: 24
}}
initialValues={{
remember: true
}}
onFinish={onFinish}
autoComplete="off">
---
</Form>
You missed sharing a complete code example, i.e. where token
is declared/set/read in the routes.tsx
file, but I don't think that is the cause of the issue. The issue is that postLogIn
is an asynchronous action and the onFinish
callback doesn't wait for it to complete, i.e. resolve, prior to redirecting to "/dashboard"
. The reason the URL changes to "/dashboard"
and nothing is rendered is because there is no !token
route matching "/dashboard"
and no general fallback to redirect to a known handled path. I'd be willing to bet there's a RRD warning in the console about no matching route found for path "/dashboard"
.
{!token ? (
<Routes>
<Route path="/" element={<Navigate to="/login" />} />
<Route path="/login" element={<Login />} />
<Route path="/signUp" element={<SignUp />} />
<Route path="/resetPassword" element={<ResetPassword />} />
// <-- no matched route for "/dashboard", render nothing
</Routes>
) : (
...
)}
dispatch(postLogIn(values));
returns a Promise object so the onFinish
should wait for Promise to resolve.
Example:
const onFinish = async (values: any) => {
try {
await dispatch(postLogIn(values));
navigate("/dashboard", { replace: true });
} catch (error) {
// catch and handle any rejected Promises or thrown errors
}
};
When you conditionally render the routes there's potential for navigating/redirecting to a conditionally rendered route prior to the condition changing and the route not actually being rendered just yet. Your code might work more smoothly if you implemented actual route protection and unconditionally render all the routes. Here's an simple example:
import { Navigate, Outlet, useLocation } from 'react-router-dom';
const ProtectedRoute = () => {
const location = useLocation();
const token = /* wherever the token value is coming from */
return token
? <Outlet />
: <Navigate to="/login" replace state={{ from: location }} />;
};
...
<Routes>
<Route path="/" element={<Navigate to="/login" />} />
<Route path="/login" element={<Login />} />
<Route path="/signUp" element={<SignUp />} />
<Route path="/resetPassword" element={<ResetPassword />} />
<Route element={<ProtectedRoute />}>
<Route element={<LayoutWithSidebarLayoutHeaderFooter />}>
<Route path="/dashboard" element={<Dashboard />} />
{role === "1" && <Route path="/groupList" element={<GroupList />} />}
<Route path="/newOrder" element={<NewOrder />} />
</Route>
</Route>
</Routes>