Search code examples
reactjsreact-router-dom

React router dom render different route based on parameter


I am currently developing a react app with an admin dashboard that has different "tabs" that I integrated them in a Router. After the admin logs in, the app navigates you to /admin. The idea of that page is that I have a side menu with different pages. Inside the AdminView I want to display the specific component based on the id that I provide:

Here is the main route that displays the admin view

<Route path="/admin">
   <Route path="login" element={<AdminLogin />} />
   <Route path=":page" element={<AdminView />} />
</Route>

Inside the Admin view I have something like this:

I am selecting the component that I want to display based in the :page:

  const getTable = () => {
    switch (table) {
      case 'realms':
        return RealmsTable;
      case 'clients':
        return ClientsTable;
      case 'users':
        return UsersTable;
      case 'roles':
        return RolesTable;
      case 'management':
        return Management;
      default:
        return NotFoundtView;
    }
  };

  const Table = useMemo(getTable, [table]);

With that component selected I am displaying it like so:

    <Layout className="dashboard-layout" hasSider>
      <DSider />
      <Layout>
        <DHeader />
        <Content>
          <Suspense>
            <Routes>
              <Route index Component={Table} />
            </Routes>
          </Suspense>
        </Content>
      </Layout>
    </Layout>

This is doing what I want, but I feel like I am doing it wrong. Is there a cleaner way?

As a reference:

I am using react 18 and react-router-dom v6

More info:

I mentioned a sider that is in sync with the selected table:

const { table } = useParams();
return (
    <Sider>
      <Menu
        className="no-bg"
        items={items}
        onClick={onMenuSelect}
        mode="inline"
        defaultSelectedKeys={[table!]}
        inlineCollapsed={collapsed}
        theme="dark"
      ></Menu>
    </Sider>
  );

It's a basic antd Menu component here and I am using the id as the keys for the menu items:

const items: MenuItem[] = [
  {
    type: 'divider',
    className: 'menu-devider'
  },
  {
    key: 'realms',
    label: 'Realms',
    className: 'menu-item',
    icon: <ReactSVG src="/svg/realms.svg" />
  },
  {
    type: 'divider',
    className: 'menu-devider'
  },
  {
    key: 'clients',
    label: 'Clients',
    className: 'menu-item',
    icon: <ReactSVG src="/svg/clients.svg" />
  },
  {
    type: 'divider',
    className: 'menu-devider'
  },
  {
    key: 'users',
    label: 'Users',
    className: 'menu-item',
    icon: <ReactSVG src="/svg/users.svg" />
  },
  {
    type: 'divider',
    className: 'menu-devider'
  },
  {
    key: 'roles',
    label: 'Roles',
    className: 'menu-item',
    icon: <ReactSVG src="/svg/roles.svg" />
  },
  {
    type: 'divider',
    className: 'menu-devider'
  },
  {
    key: 'management',
    label: 'Management',
    className: 'menu-item',
    icon: <ReactSVG src="/svg/management.svg" />
  },
  {
    type: 'divider',
    className: 'menu-devider'
  }
];


Solution

  • My suggestion would be to make AdminView a layout route component that renders an Outlet component instead of an index route and each table component on its own nested route. This should remove the need for the getTable component matcher and component reference memoization. In other words, let React-Router do the matching/rendering work for you, no need to "re-invent the wheel".

    Example:

    import { Outlet } from 'react-router-dom';
    
    ...
    
    <Layout className="dashboard-layout" hasSider>
      <DSider />
      <Layout>
        <DHeader />
        <Content>
          <Suspense>
            <Outlet /> // <-- nested routes render content here
          </Suspense>
        </Content>
      </Layout>
    </Layout>
    
    <Route path="/admin">
      <Route path="login" element={<AdminLogin />} />
      <Route element={<AdminView />}>
        <Route path="realms" element={<RealmsTable />} />
        <Route path="clients" element={<ClientsTable />} />
        <Route path="users" element={<UsersTable />} />
        <Route path="roles" element={<RolesTable />} />
        <Route path="management" element={<RealmsTable />} />
      </Route>
      <Route path="*" element={<NotFoundtView />} />
    </Route>