I have Manage Items page where can see items in a table with two actions as Edit
and Delete
.
Required, on click of edit/delete button the Edit/Delete component should be rendered with url like manage-items/edit/1
or manage-items/edit?id=1
Currently:
- Base URL:
manage-items
Required:
- Edit URL:
manage-items/edit/1
ormanage-items/edit?id=1
- Delete URL:
manage-items/delete/1
ormanage-items/delete?id=1
Tried below code.
const ManageItems = () => {
...
return (
<div>
{edit ? <Routes> <Route path="/edit/:itemId" element={<EditItem itemId={itemId}/>} /> </Routes>
: delete ? <Routes> <Route path="/delete/:itemId" element={<DeleteItem itemId={itemId}/>} /> </Routes>
: <ItemTable items={items}/>}
</div>
);
};
When both the flags edit
and delete
are false then I am able to see item table but on click none of the other components Edit or Delete is rendering, nor the URL is changing.
Its just empty white space there.
If I remove and then can see both Edit and Delete components accordingly but no effect on the URL.
Nee to change the URL accordingly on click.
Other code:
const App = () => {
return (
<div id="App" className="app-wrapper">
<div>
<Suspense fallback={<Loader />}>
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Routes>
{/* public routes */}
<Route path={PORTAL_PATH_BASE} element={<Login />} />
<Route
path={PORTAL_PATH_UNAUTHORIZED}
element={<ErrorFallback error={COMMON_ERRORS.unauthorized} />}
/>
<Route
path={PORTAL_PATH_PAGENOTFOUND}
element={<ErrorFallback error={COMMON_ERRORS.pageNotFound} />}
/>
<Route
path={PORTAL_PATH_LOGINCALLBACK}
element={<LoginCallback />}
/>
{/* we want to protect these routes */}
<Route element={<RequireAuth />}>
<Route
path={`${PORTAL_PATH_BASE}/*`}
element={
<WithAxios>
<MyAdmin />
</WithAxios>
}
/>
</Route>
</Routes>
</ErrorBoundary>
</Suspense>
</div>
</div>
);
};
const MyAdmin = () => {
const { pathname } = useLocation();
const [headerNavItems, setHeaderNavItems] = useState();
useEffect(() => {
let headerMenuCodes =
JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS.UserInfo)).menu.map(
(m) => m.menuCode
) || [];
setHeaderNavItems(
NAV_ITEM_LIST.filter(
(hdr) => headerMenuCodes.includes(hdr.key) || hdr.key === "XY"
)
);
}, []);
return (
<div>
<CustomHeader navItemList={headerNavItems} />
<PageBanner
Title={
MANAGE_ROUTES.find((route) => route.path === pathname)
?.title
}
>
<CustomBreadcrumb />
</PageBanner>
<ScrollToTop>
<Routes>
<Route path={PORTAL_SUB_PATH_HOME} element={<Home />} />
<Route
path={`${PORTAL_SUB_PATH_BPR}/*`}
element={<BuildingProgress />}
/>
<Route path={`${PORTAL_SUB_PATH_MANAGE}/*`} element={<ManageItems />} />
<Route path={`${PORTAL_SUB_PATH_RPT}/*`} element={<Settings />} />
<Route path="*" element={<PageNotFound />} />
</Routes>
</ScrollToTop>
</div>
);
};
const DeleteItem = ({itemId}) => {
... load item in readonly fields and allow to confirm delete ...
}
const ItemTable = ({items, onEdit, onDelete}) => {
... Show table of items with two action buttons as Edit and Delete ...
}
const EditItem = ({itemId}) => {
... load item in editable fields and allow to save change ...
}
2
Answers
You need to declare your routes in the root App/index component.
Something like this.
Sample code sandbox: https://codesandbox.io/s/black-bash-hcvukk
It’s unclear what
edit
,delete
, anditemId
are in theManageItems
component, but I suspect these are entirely unnecessary, just render the content on unconditional routes and letreact-router-dom
do it’s "thing" and render the best match. The issue that happens when you conditionally render routes is that the state they depend on hasn’t been updated by React and the component rerendered prior to the navigation action is processed that navigates to the route path. In other words, the route isn’t mounted to navigate to.I also suspect that you really want
EditItem
andDeleteItem
to get theitemId
value from the route path param and not as a passed prop.This assumes you’ve some UI logic somewhere that is navigating between these sub-routes to switch between the edit/delete and the base index routes, e.g. the different "display modes".
Example: