I am trying to make authentication for a React JS project with react router v6, but I am getting the following error: When I sign in:
Warning: React has detected a change in the order of Hooks called by App. This will lead to bugs and errors if not fixed.
Rendered more hooks than during the previous render.
When I sign out:
Warning: React has detected a change in the order of Hooks called by App. This will lead to bugs and errors if not fixed.
Rendered fewer hooks than expected. This may be caused by an accidental early return statement.
Here's my code: Routes.jsx
import AddNewProductPage from "pages/AddNewProductPage"
import React from "react"
import { Navigate, Route, useLocation } from "react-router-dom"
import {
Homepage,
SignInPage,
ProductListManagement,
ProductDetailPage,
SignUpPage,
ProductListPage,
ForgetPasswordPage,
ShoppingCartPage,
} from "pages"
import AdminPage from "pages/AdminPage"
import LoadingSpinner from "components/LoadingSpinner/LoadingSpinner"
import { useSelector } from "react-redux"
const DefaultComponent = () => {
return <Navigate to={Routes.home.path} />
}
export const Routes = {
signIn: {
path: "/sign-in",
element: SignInPage,
},
signUp: {
path: "/sign-up",
element: SignUpPage,
},
forgetPassword: {
path: "/forget-password",
element: ForgetPasswordPage,
},
admin: {
path: "/admin",
element: AdminPage,
isAuth: true,
isAdmin: true,
index: ProductListManagement,
routes: {
productList: {
path: "products",
element: ProductListManagement,
isAuth: true,
},
addProduct: {
path: "add-product",
element: AddNewProductPage,
isAuth: true,
},
editProduct: {
path: "edit-product/:id",
element: AddNewProductPage,
isAuth: true,
},
default: {
path: "*",
element: DefaultComponent,
},
},
},
shoppingCart: {
path: "/cart",
isAuth: true,
element: ShoppingCartPage,
index: ShoppingCartPage,
routes: {
default: {
path: "*",
element: ShoppingCartPage,
},
},
},
home: {
path: "/",
element: Homepage,
index: ProductListPage,
routes: {
products: {
path: "products",
element: ProductListPage,
},
productDetail: {
path: "products/:id",
element: ProductDetailPage,
},
default: {
path: "*",
element: DefaultComponent,
},
},
},
default: {
path: "*",
element: DefaultComponent,
},
}
export function AppLoading(props) {
return <Route path={props.path} element={<LoadingSpinner />} />
}
export function RouteComponentWrapper(route, key) {
const user = useSelector((state) => state.user)
let location = useLocation()
if (!user.isAuth && route.isAuth)
return (
<Route
key={key}
path={route.path}
element={
<Navigate to={Routes.signIn.path} state={{ from: location }} />
}
/>
)
if (!user.isAdmin && route.isAdmin) {
if (user.isAuth) {
return (
<Route
key={key}
path={route.path}
element={
<Navigate to={Routes.home.path} state={{ from: location }} />
}
/>
)
} else {
return (
<Route
key={key}
path={route.path}
element={
<Navigate to={Routes.signIn.path} state={{ from: location }} />
}
/>
)
}
}
return (
<Route path={route.path} element={<route.element />} key={key}>
{route.index ? <Route index element={<route.index />} /> : undefined}
{route.routes ? RenderRoutes(route.routes) : undefined}
</Route>
)
}
export function RenderRoutes(routes) {
return Object.values(routes).map((route, index) => {
console.log(index, route.path)
return RouteComponentWrapper(route, index)
})
}
userSlice.js
import { createSlice } from "@reduxjs/toolkit"
const initialState = {
userInfo: {
username: "",
email: "",
},
token: "",
isAuth: false,
isAdmin: false,
}
export const userSlice = createSlice({
name: "user",
initialState,
reducers: {
signIn: (state, action) => {
state.userInfo.email = action.payload.userInfo.email
state.token = action.payload.token
state.isAuth = true
state.isAdmin = true
},
signInAsAdmin: (state, action) => {
state.userInfo = action.payload.userInfo
state.token = action.payload.token
state.isAuth = true
state.isAdmin = true
},
signOut: () => {
//state.isAuth = false
return initialState
},
forgetPassword: () => {},
},
})
export const { signIn, signInAsAdmin, signOut, forgetPassword } =
userSlice.actions
export default userSlice.reducer
App.jsx
import React from "react"
import { Routes as RoutesDom } from "react-router-dom"
import { QueryClient, QueryClientProvider } from "react-query"
import { RenderRoutes } from "routes/Routes"
import { Routes } from "routes/Routes"
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<RoutesDom>{RenderRoutes(Routes)}</RoutesDom>
</QueryClientProvider>
)
}
export default App
Some console.log if necessary
Sign in
//First render
Routes.jsx:170 0 '/sign-in'
Routes.jsx:170 1 '/sign-up'
Routes.jsx:170 2 '/forget-password'
Routes.jsx:170 3 '/admin'
Routes.jsx:170 4 '/cart'
Routes.jsx:170 5 '/'
Routes.jsx:170 0 'products'
Routes.jsx:170 1 'products/:id'
Routes.jsx:170 2 '*'
Routes.jsx:170 6 '*'
//Second render
Routes.jsx:170 0 '/sign-in'
Routes.jsx:170 1 '/sign-up'
Routes.jsx:170 2 '/forget-password'
Routes.jsx:170 3 '/admin'
Routes.jsx:170 0 'products'
Routes.jsx:170 1 'add-product'
Routes.jsx:170 2 'edit-product/:id'
Routes.jsx:170 3 '*'
Routes.jsx:170 4 '/cart'
Routes.jsx:170 0 '*'
Routes.jsx:170 5 '/'
Error
Sign out
//First render
Routes.jsx:170 0 '/sign-in'
Routes.jsx:170 1 '/sign-up'
Routes.jsx:170 2 '/forget-password'
Routes.jsx:170 3 '/admin'
Routes.jsx:170 0 'products'
Routes.jsx:170 1 'add-product'
Routes.jsx:170 2 'edit-product/:id'
Routes.jsx:170 3 '*'
Routes.jsx:170 4 '/cart'
Routes.jsx:170 0 '*'
Routes.jsx:170 5 '/'
Routes.jsx:170 0 'products'
Routes.jsx:170 1 'products/:id'
Routes.jsx:170 2 '*'
Routes.jsx:170 6 '*'
//Second render
Routes.jsx:170 0 '/sign-in'
Routes.jsx:170 1 '/sign-up'
Routes.jsx:170 2 '/forget-password'
Routes.jsx:170 3 '/admin'
Routes.jsx:170 4 '/cart'
Routes.jsx:170 5 '/'
Routes.jsx:170 0 'products'
Routes.jsx:170 1 'products/:id'
Routes.jsx:170 2 '*'
Routes.jsx:170 6 '*'
Error