Faced the problem of unloading from localStorage, perhaps even with an understanding of the work of React itself, namely:
import "bootstrap/dist/css/bootstrap.min.css";
import { React, useState, useContext } from "react";
import {
Form,
Row,
InputGroup,
Col,
Button,
Container,
Card,
} from "react-bootstrap";
import { AuthContext } from "../components/AuthContext.jsx";
import { Navigate } from "react-router-dom";
function LoginPage() {
const { setOperator } = useContext(AuthContext);
const [validate, setValidate] = useState(true);
const [formData, setFormData] = useState({ login: "", pass: "" });
const handleChange = (event) => {
setFormData({ ...formData, [event.target.name]: event.target.value });
};
const handleButton = () => {
fetch("/set_operator", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
login: formData.login,
pass: formData.pass,
}),
})
.then((res) => res.json())
.then((userData) => {
/* console.log(userData); */
if (userData.error === "User is not found") {
return console.log(userData);
}
setOperator(userData.operator);
localStorage.setItem("currentUser", JSON.stringify(userData.operator));
});
};
const checkStorage = localStorage.hasOwnProperty("currentUser");
if (checkStorage) {
return <Navigate to={"/admin"} />;
}
return (
<Container>
<Row className="justify-content-center align-items-center g-4">
<Col xs={5}>
<Card border="primary" style={{ width: "35rem" }}>
<Card.Header className="text-center">Authentication</Card.Header>
<Form noValidate validated={validate} /* onSubmit={handleSubmit} */>
<Row className="mb-3">
<Form.Group
as={Col}
md="7"
controlId="validationCustomUsername"
>
<Form.Label>Username</Form.Label>
<InputGroup hasValidation>
<InputGroup.Text id="inputGroupPrepend">@</InputGroup.Text>
<Form.Control
required
type="text"
placeholder="login"
name="login"
value={formData.login}
onChange={handleChange}
/>
<Form.Control.Feedback>
Username is a required field{" "}
</Form.Control.Feedback>
</InputGroup>
</Form.Group>
</Row>
<Row className="mb-3">
<Form.Group as={Col} md="7" controlId="validationCustomPass">
<Form.Label>Password</Form.Label>
<Form.Control
required
type="text"
placeholder="password"
name="pass"
value={formData.pass}
onChange={handleChange}
/>
<Form.Control.Feedback>
Password is a required field{" "}
</Form.Control.Feedback>
</Form.Group>
</Row>
<Button onClick={handleButton} href="/admin">
Login
</Button>
</Form>
</Card>
</Col>
</Row>
</Container>
);
}
export default LoginPage;
What happens here is writing to localStorage works crookedly, I constantly catch looping errors, then I want to receive data from localStorage using useContext and reducer My context:
import React, { createContext, useReducer, useEffect } from "react";
import { reducerUsers } from "./ReducerUsers";
export const AuthContext = createContext();
const initialState = {
operators: null,
};
export const AuthProvider = ({ children }) => {
const [value, dispatch] = useReducer(reducerUsers, initialState);
useEffect(() => {
dispatch({ type: "CHECK_LOCAL_STORAGE" });
}, [children]);
value.setOperator = (operatorData) => {
dispatch({ type: "SET_OPERATOR", payload: operatorData });
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
its my reducer:
export function reducerUsers(state, { type, payload }) {
switch (type) {
case "SET_OPERATOR":
return {
...state,
operators: payload || [],
};
case "CHECK_LOCAL_STORAGE":
const storedUser = JSON.parse(localStorage.getItem("currentUser"));
if (storedUser) {
return {
...state,
operators: storedUser,
};
}
return state;
default:
return state;
}
}
Next, I create a private route, which I write the following:
import React, { useContext } from "react";
import { Outlet, Navigate } from "react-router-dom";
import { AuthContext } from "../components/AuthContext";
export const PrivateRoute = ({
element: Element,
currentUser,
roles,
...rest
}) => {
const { operators } = useContext(AuthContext);
console.log(operators);
if (!currentUser) {
// console.log(currentUser);
return <Navigate to="/login" />;
}
if (roles && !roles.includes(currentUser[0].role)) {
// console.log(`${currentUser[0].role} ${roles}`);
return <Navigate to="*" />;
}
return (
<Outlet>
<Element {...rest} />
</Outlet>
);
};
Do not look that the checks are now implemented through props, it's just like a gag from hopelessness... But if you look at the console.log(operators); then first of all I get null
and then an array of data
0 : id : 1 img : null login : "admin" name : "Alen" pass : "admin" role : "admin" [[Prototype]] : Object length : 1 [[Prototype]] : Array(0)
Accordingly, I can no longer make any checks, since I catch an error immediately when I go or refresh the page. And finally, my App file for the full picture of the call
import React, { useState } from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import { ProductsContext } from "./components/ProductsContext.jsx";
import { AuthProvider } from "./components/AuthContext.jsx";
import { PrivateRoute } from "./components/PrivateRoute.jsx";
import Navibar from "./components/NaviBar.jsx";
import LoginPage from "./pages/LoginForm.jsx";
import Notfoundpage from "./pages/Notfoundpage.jsx";
import Infoproduct from "./pages/Infoproduct.jsx";
import Productpage from "./pages/Productpage.jsx";
import { PaymentPage } from "./pages/Payment.jsx";
import { BucketList } from "./pages/BucketList.jsx";
import { OrderSuccessPage } from "./pages/OrderSuccess.jsx";
import FoodDrink from "./pages/FoodDrink.jsx";
import AdminPage from "./pages/AdminPage.jsx";
import { AddProduct } from "./pages/AddProduct.jsx";
import { Orders } from "./pages/Orders.jsx";
import { ProductList } from "./pages/ProductList.jsx";
import Sidebar from "./components/SideBar.jsx";
function App() {
const [currentUser, setCurrentUser] = useState(
JSON.parse(localStorage.getItem("currentUser"))
);
return (
<>
<AuthProvider>
<ProductsContext>
<Router>
<Navibar />
<Sidebar />
<Routes>
<Route path="/" element={<Productpage />} />
<Route path="product/:id" element={<Infoproduct />} />
<Route path=":typeParam" element={<FoodDrink />} />
<Route path="payment" element={<PaymentPage />} />
<Route path="bucket" element={<BucketList />} />
<Route path="order_success" element={<OrderSuccessPage />} />
<Route path="login" element={<LoginPage />} />
<Route
path="/admin"
element={
<PrivateRoute
element={AdminPage}
roles={["admin", "moderator"]}
currentUser={currentUser}
/>
}
/>
<Route path="/admin/add_product" element={<AddProduct />} />
<Route path="/admin/orders" element={<Orders />} />
<Route path="/admin/product_list" element={<ProductList />} />
<Route path="*" element={<Notfoundpage />} />
</Routes>
</Router>
</ProductsContext>
</AuthProvider>
</>
);
}
export default App;
Please help me understand what I'm doing wrong, how to do it better? I'm just learning React and don't understand my main mistake. I would be very grateful for any help version my react: "react": "^18.2.0" version my react-router-dom: "react-router-dom": "^6.11.1"
Update:
when I want to get data from the localStorage from my component, I get null first of all, which prevents me from further work. For example, if you look at the logs, they look like this:
if I do a check in PrivateRoute, or refer to the role element. I will get an error
const { operators } = useContext(AuthContext);
console.log(operators[0].role);
if (!operators) {
// console.log(currentUser);
return <Navigate to="/login" />;
}
and I don't understand why this is happening. And how do I properly get my data and process it