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;
2
Answers
The issue you’re experiencing is likely due to the asynchronous nature of updating the userrole state. The route rendering logic doesn’t pick up the updated userrole because React doesn’t re-render routes unless there is a state change triggering the re-render.
Make sure that the userrole state is set correctly before rendering the Routes. Use conditional rendering to wait until the userrole is ready
Issue
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:
The
App
component mounts and:userrole
to""
"user"
value from localStorage and declares aUser
variableuseEffect
hook runs and updates theuserRole
state to the currentUser?.role
value, likelynull
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 theuserrole
stateSolution Suggestion
Pass 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 inApp
via a side-effect.Example:
The
Login
andRegistration
receivesetUser
as a prop so that when users log in or register you can update theuser
state in the parent component and trigger a component rerender.