Hello dear stackoverflow users. First of all, thank you in advance for your help. I have a react application in this application, I check the user login process when going to the /admin page, if the user is not logged in, I redirect to the /login page. I log in the user by making a request on the backend and create a token. If the user login is successful, I try to redirect to the /admin page, but I get an error. I haven't been able to solve it yet. I am sharing my codes with you.
router/index.jsx
import { createBrowserRouter } from "react-router-dom";
import WebLayout from "~/../layouts/web";
import AdminLayout from "~/../layouts/admin";
import Home from "~/pages/web/home";
import Orders from "~/pages/admin/orders";
import Categories from "~/pages/admin/categories";
import Tables from "~/pages/admin/tables";
import Meals from "~/pages/admin/meals";
import AdminHome from "~/pages/admin/home";
import MealDetail from "~/pages/web/mealdetail";
import Login from "~/components/shared/login";
import PrivateRoute from "~/components/shared/privateroute";
const routes = createBrowserRouter([
{
path: "/login",
element: <Login />,
},
{
path: "/",
element: <WebLayout />,
children: [
{
index: true,
element: <Home />,
},
{
path: "meal/:mealId",
element: <MealDetail />,
},
],
},
{
path: "/admin",
element: <AdminLayout />,
children: [
{
index: true,
element: <PrivateRoute component={AdminHome} />,
},
{
path: "orders",
element: <PrivateRoute component={Orders} />,
},
{
path: "categories",
element: <PrivateRoute component={Categories} />,
},
{
path: "tables",
element: <PrivateRoute component={Tables} />,
},
{
path: "meals",
element: <PrivateRoute component={Meals} />,
},
],
},
]);
export default routes;
privateroute/index.jsx
import React from "react";
import { Route, Navigate } from "react-router-dom";
import { useSelector } from "react-redux";
const PrivateRoute = ({ element: Element, ...rest }) => {
const isAuthenticated = useSelector((state) => state.auth.isAuthenticated);
return isAuthenticated ? (
<Route {...rest} element={<Element />} />
) : (
<Navigate to="/login" />
);
};
export default PrivateRoute;
login/index.jsx
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { loginAsync } from "~/store/authSlice";
import { useNavigate } from "react-router-dom"; // useNavigate hook'unu ekleyin
const Login = () => {
const dispatch = useDispatch();
const navigate = useNavigate(); // useNavigate hook'unu tanımlayın
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const handleLogin = async () => {
try {
// Redux Toolkit ile login işlemini gerçekleştiren action çağrısı
await dispatch(loginAsync({ username, password }));
// Login başarılı olduğunda /admin sayfasına yönlendir
navigate("/admin"); // useNavigate hook'unu kullanarak yönlendirme işlemini gerçekleştirin
} catch (error) {
console.error("Login error:", error.message);
// Handle login error, display a message to the user, etc.
}
};
return (
<div>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={handleLogin}>Login</button>
</div>
);
};
export default Login;
authslice.js
// src/store/authSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
const initialState = {
token: null,
username: null,
isAuthenticated: false,
};
export const loginAsync = createAsyncThunk('auth/login', async ({ username, password }) => {
try {
const response = await axios.post('http://localhost:3000/api/login', {
username,
password,
});
console.log(response.data);
const data = response.data;
return { token: data.token, username };
} catch (error) {
throw new Error(error.response.data.error);
}
});
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
logout(state) {
state.token = null;
state.username = null;
state.isAuthenticated = false;
},
},
extraReducers: (builder) => {
builder.addCase(loginAsync.fulfilled, (state, action) => {
const { token, username } = action.payload;
state.token = token;
state.username = username;
state.isAuthenticated = true;
});
},
});
export const { logout } = authSlice.actions;
export default authSlice.reducer;
and finally the error i got
Unexpected Application Error!
A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.
Error: A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.
at invariant (http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=9572ed87:202:11)
at Route (http://localhost:5173/node_modules/.vite/deps/react-router-dom.js?v=9572ed87:3706:10)
at renderWithHooks (http://localhost:5173/node_modules/.vite/deps/chunk-UHLQBSTO.js?v=9572ed87:12171:26)
at mountIndeterminateComponent (http://localhost:5173/node_modules/.vite/deps/chunk-UHLQBSTO.js?v=9572ed87:14921:21)
at beginWork (http://localhost:5173/node_modules/.vite/deps/chunk-UHLQBSTO.js?v=9572ed87:15902:22)
at beginWork$1 (http://localhost:5173/node_modules/.vite/deps/chunk-UHLQBSTO.js?v=9572ed87:19749:22)
at performUnitOfWork (http://localhost:5173/node_modules/.vite/deps/chunk-UHLQBSTO.js?v=9572ed87:19194:20)
at workLoopSync (http://localhost:5173/node_modules/.vite/deps/chunk-UHLQBSTO.js?v=9572ed87:19133:13)
at renderRootSync (http://localhost:5173/node_modules/.vite/deps/chunk-UHLQBSTO.js?v=9572ed87:19112:15)
at recoverFromConcurrentError (http://localhost:5173/node_modules/.vite/deps/chunk-UHLQBSTO.js?v=9572ed87:18732:28)
💿 Hey developer 👋
You can provide a way better UX than this when your app throws errors by providing your own ErrorBoundary or errorElement prop on your route.
Change your route to:
element: <PrivateRoute>YourComponents</PrivateRoute>,
and private Route return statement from
return isAuthenticated ? (
<Route {...rest} element={<Element />} />
) : (
<Navigate to="/login" />
);
to
return isAuthenticated ? (
<>
{children}
<Outlet />
</> ) : (
<Navigate to="/login" />
);
Once you render the route, your private route element should render the children in the private route and the Outlet for other nested routes.