I'm trying to route to a component based on a condition and it does it, but only if I refresh the page. I've had to add forceUpdate()
to get it to work, but that is killing performance. Maybe this is something that would stand out easily for you guys, but for me it is escaping me.
The following route is the one in question:
<Route element={<RequireAuth />}>
<Route
path="/"
element={userrole === "Browser" ? <ViewOnly /> : <InvoiceList2 />}
/>
</Route>
Full code:
import React, { useEffect, useState, useReducer } from "react";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import {
useFetchVendorsQuery,
useFetchInvoicesQuery,
useFetchTrackersQuery,
} from "../src/features/Invoices/InvoiceSlice";
const ROLES = {
User: "user",
Admin: "Admin",
};
const Wildcard = () => {
<Navigate to="/login" />;
};
const App = () => {
// const auth = useAuth();
// let { user, role, userid } = auth.auth;
const GetVendors = useFetchVendorsQuery();
const [vendors, setVendors] = useState([]);
const GetTrackers = useFetchTrackersQuery();
const [trackers, setTrackers] = useState([]);
const GetInvoices = useFetchInvoicesQuery();
const [invoices, setInvoices] = useState([]);
const [userrole, setUserRole] = useState("");
const User = JSON.parse(localStorage.getItem("user"));
const [ignored, forceUpdate] = useReducer((x) => x + 1, 0);
const handleOnload = () => {
forceUpdate();
};
useEffect(() => {
// setVendors(GetVendors);
// setInvoices(GetInvoices);
// setTrackers(GetTrackers);
setUserRole(User?.role);
forceUpdate();
}, [userrole, ignored]);
return (
<div className="content">
<BrowserRouter>
{/* <Header /> */}
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Registration />} />
<Route element={<RequireAuth />}>
{/* <Route path="/" element={<InvoiceList2 />} /> */}
<Route
path="/"
element={userrole === "Browser"
? <ViewOnly />
: <InvoiceList2 />
}
/>
</Route>
<Route element={<RequireAuth />}>
<Route path="/admin" element={<Admin />} />
</Route>
<Route element={<RequireAuth />}>
<Route path="/manual" element={<ManualEntry />} />
</Route>
<Route path="/*" element={Wildcard} />
</Routes>
<Footer1 />
</BrowserRouter>
</div>
);
};
export default App;
I suspect the issue is that users are authenticating and this only updates the "user"
value in localStorage and nothing else. LocalStorage is non-reactive.
The issues is basically these lines of code:
const [userrole, setUserRole] = useState(""); const User = JSON.parse(localStorage.getItem("user")); useEffect(() => { // setVendors(GetVendors); // setInvoices(GetInvoices); // setTrackers(GetTrackers); setUserRole(User?.role); forceUpdate(); }, [userrole, ignored]);
The App
component mounts and:
userrole
to ""
"user"
value from localStorage and declares a User
variableuseEffect
hook runs and updates the userRole
state to the current User?.role
value, likely null
since no user is authenticatedI suspect the sequence of events goes something along the lines of:
"user"
value is set in localStorageApp
to rerender and "see" the updated "user"
value in localStorageUser
and the effects runs to update the userrole
statePass the user state updater function down as props to your Login
component so that when a user authenticates you update the React state instead of localStorage. If you need to persist that state, then do that in App
via a side-effect.
Example:
const App = () => {
...
// Lazy initialize user state from localStorage
const [user, setUser] = useState(() => {
return localStorage.getItem("user")
? JSON.parse(localStorage.getItem("user"))
: null;
});
// Persist user state changes to localStorage
useEffect(() => {
localStorage.setItem("user", JSON.stringify(user));
}, [user]);
return (
<div className="content">
<BrowserRouter>
...
<Routes>
<Route
path="/login"
element={<Login setUser={setUser} />}
/>
<Route
path="/register"
element={<Registration setUser={setUser} />}
/>
<Route element={<RequireAuth />}>
<Route
path="/"
element={user.role === "Browser"
? <ViewOnly />
: <InvoiceList2 />
}
/>
<Route path="/admin" element={<Admin />} />
<Route path="/manual" element={<ManualEntry />} />
</Route>
<Route path="*" element={<Navigate to="/login" />} />
</Routes>
...
</BrowserRouter>
</div>
);
};
The Login
and Registration
receive setUser
as a prop so that when users log in or register you can update the user
state in the parent component and trigger a component rerender.