skip to Main Content

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 or manage-items/edit?id=1
  • Delete URL: manage-items/delete/1 or manage-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


  1. You need to declare your routes in the root App/index component.
    Something like this.

    import { Routes, Route, Link } from "react-router-dom";
    import "./styles.css";
    
    export default function App() {
      return (
        <div className="App">
          <h1>App Header</h1>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/manage-items">
              <Route index element={<ManageItems />} />
              <Route path="edit/:id" element={<h2>Edit</h2>} />
              <Route path="delete/:id" element={<h2>Delete</h2>} />
            </Route>
            <Route path="*" element={<h2>Not Found</h2>} />
          </Routes>
        </div>
      );
    }
    
    const Home = () => {
      return (
        <>
          <h2>Home</h2>
          <Link to="/manage-items">Go to Items</Link>
        </>
      );
    };
    
    const ManageItems = () => {
      return (
        <>
          <h1>Manage Items</h1>
          <div>
            <Link to="edit/1">Edit</Link>
          </div>
          <div>
            <Link to="delete/1">Delete</Link>
          </div>
        </>
      );
    };
    

    Sample code sandbox: https://codesandbox.io/s/black-bash-hcvukk

    Login or Signup to reply.
  2. It’s unclear what edit, delete, and itemId are in the ManageItems component, but I suspect these are entirely unnecessary, just render the content on unconditional routes and let react-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 and DeleteItem to get the itemId 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:

    const ManageItems = () => {
      ...
      return (
        <div>
          <Routes>
            <Route
              // "/{PORTAL_PATH_BASE}/{PORTAL_SUB_PATH_MANAGE}/edit/:itemId"
              path="/edit/:itemId"
              element={<EditItem />}
            />
            <Route
              // "/{PORTAL_PATH_BASE}/{PORTAL_SUB_PATH_MANAGE}/delete/:itemId"
              path="/delete/:itemId"
              element={<DeleteItem />}
            />
            <Route
              // "/{PORTAL_PATH_BASE}/{PORTAL_SUB_PATH_MANAGE}"
              index
              element={<ItemTable items={items} />}
            />
          </Routes>
        </div>
      );
    };
    
    const EditItem = () => {
      const { itemId } = useParams();
    
      ...
    };
    
    const DeleteItem = () => {
      const { itemId } = useParams();
    
      ...
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search