Search code examples
react-routerreact-router-dom

Why does this code not navigate to the subpage?


I am migrating an app from react-router-dom v5 to v6. The v5 app works by clicking buttons to navigate to a sub page. It changed the page by doing a history.push. Now I am trying the v6 way but it does not work as expected. I even see the desired url in the address bar but the page does not render. I have read that the navigate hook should be called in an event handler which I have in the demo code.

What am I missing?

How should I debug this?

import React from 'react';
import { BrowserRouter as Router, Navigate, Route, Routes, useNavigate } from 'react-router-dom';

function ListOfThings() {

  const navigate = useNavigate();

  function handleClickCreate( e:React.MouseEvent<HTMLElement>) {
    navigate( 'CreateNewThing' );
  }

  function handleClickShow( e:React.MouseEvent<HTMLElement>) {
    navigate( 'ShowThing' );
  }

  return (
    <>
      <h2>ListOfThings</h2>
      <button onClick={handleClickCreate}>Go To Create New Thing Page</button>
      <button onClick={handleClickShow}>Go To Show Thing Page</button>
    </>
  );
}

function CreateNewThing() {

  const navigate = useNavigate();

  function handleClick() {
    navigate( '/ListOfThings' );
  }

  return (
    <>
      <h2>Create New Thing Page</h2>
      <button onClick={handleClick}>Go To List Of Things</button>
    </>
  );
}

function ShowThing() {

  const navigate = useNavigate();

  function handleClick() {
    navigate( '/ListOfThings' );
  }

  return (
    <>
      <h2>Show Thing Page</h2>
      <button onClick={handleClick}>Go To List Of Things</button>
    </>
  );
}

function App() {
  return (
    <Router>
      <Routes>
        <Route path='/ListOfThings' element={<ListOfThings />} >
          <Route path='CreateNewThing' element={<CreateNewThing />} />
          <Route path=':id' element={<ShowThing />} />
        </Route>
        <Route path='/' element={ <Navigate to='ListOfThings' replace /> } />
      </Routes>
    </Router>
  );
}

export default App;


Solution

  • The SubPage component is rendered by a nested route, the parent layout route component necessarily should render an Outlet for the nested routes to render their element content into.

    import { Outlet, useNavigate } from 'react-router-dom';
    
    function Home() {
      const navigate = useNavigate();
    
      function handleClick() {
        navigate('Subpage');
      }
    
      return (
        <>
          <h2>Home</h2>
          <button onClick={handleClick}>Go To Sub Page</button>
          <div>
            <Outlet /> // <-- nested route content rendered here
          </div>
        </>
      );
    }
    
    function App() {
      return (
        <Router>
          <Routes>
            <Route path='/Home' element={<Home />}>
              <Route path='Subpage' element={<SubPage />} /> // <-- rendered into Outlet
            </Route>
            <Route path='/' element={<Navigate to='Home' replace />} />
          </Routes>
        </Router>
      );
    }
    

    For more information see:

    If you prefer that the Home and SubPage components be rendered individually then move the Home component off the layout route and render it as an index route.

    function App() {
      return (
        <Router>
          <Routes>
            <Route path='/Home'>
              <Route index element={<Home />} />             // "/home"
              <Route path='Subpage' element={<SubPage />} /> // "/home/subpage"
            </Route>
            <Route path='/' element={<Navigate to='Home' replace />} />
          </Routes>
        </Router>
      );
    }