remix-run / react-router

Declarative routing for React
https://reactrouter.com
MIT License
53k stars 10.3k forks source link

[Bug]: useParams Object Wildcard Prop with Descendant Routes #8720

Closed gallinaromaurizio closed 2 years ago

gallinaromaurizio commented 2 years ago

What version of React Router are you using?

6.2.2

Steps to Reproduce

In Main.js :

<Routes>
  <Route path='/hello/*' element={<Suspense fallback={<Fall/>}><Hello/></Suspense>}/>
</Routes>

In Hello.js

<Routes>
  <Route path=":name" element={<Menu/>} />
</Routes>

Test Case Starter:

import * as React from 'react';
import { Routes, Route, Outlet, Link, useParams } from 'react-router-dom';

export default function App() {
  return (
    <div>
      <h1>Basic Example</h1>

      <p>
        This example demonstrates some of the core features of React Router
        including nested <code>&lt;Route&gt;</code>s,{' '}
        <code>&lt;Outlet&gt;</code>s, <code>&lt;Link&gt;</code>s, and using a
        "*" route (aka "splat route") to render a "not found" page when someone
        visits an unrecognized URL.
      </p>

      {/* Routes nest inside one another. Nested route paths build upon
            parent route paths, and nested route elements render inside
            parent route elements. See the note about <Outlet> below. */}
      <Routes>
        <Route path="/" element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="about" element={<About />} />
          <Route path="dashboard" element={<Dashboard />} />
          <Route path="hello/*" element={<Hello />} />
          {/* Using path="*"" means "match anything", so this route
                acts like a catch-all for URLs that we don't have explicit
                routes for. */}
          <Route path="*" element={<NoMatch />} />
        </Route>
      </Routes>
    </div>
  );
}
function Hello() {
  const pars = useParams();
  console.log(pars)
  return (
    <Routes>
      <Route path=":name" element={<div>{pars.name}</div>} />
    </Routes>
  );
}

function Layout() {
  return (
    <div>
      {/* A "layout route" is a good place to put markup you want to
          share across all the pages on your site, like navigation. */}
      <nav>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/dashboard">Dashboard</Link>
          </li>
          <li>
            <Link to="/nothing-here">Nothing Here</Link>
          </li>
          <li>
            <Link to="/hello/james">Hello</Link>
          </li>
        </ul>
      </nav>

      <hr />

      {/* An <Outlet> renders whatever child route is currently active,
          so you can think about this <Outlet> as a placeholder for
          the child routes we defined above. */}
      <Outlet />
    </div>
  );
}

function Home() {
  return (
    <div>
      <h2>Home</h2>
    </div>
  );
}

function About() {
  return (
    <div>
      <h2>About</h2>
    </div>
  );
}

function Dashboard() {
  return (
    <div>
      <h2>Dashboard</h2>
    </div>
  );
}

function NoMatch() {
  return (
    <div>
      <h2>Nothing to see here!</h2>
      <p>
        <Link to="/">Go to the home page</Link>
      </p>
    </div>
  );
}

Expected Behavior

useParams should return an object with the property name matching the name of the dynamic param :hello :

{hello: 'James'}

Actual Behavior

useParams returns an object with a wildcard character:

{*:'James'}

If I add more segments to this route I still have one wildcard prop object and the entire path along with it:

{*:'James/credit-cards/1'}

IDrissAitHafid commented 2 years ago

I think this should work:

function Hello() {
  return (
    <Routes>
      <Route path=":name" element={<HelloChild/>} />
    </Routes>
  );
}

function HelloChild() {
  const pars = useParams();
  console.log(pars);
  return (<div>{pars.name}</div>);
}
timdorr commented 2 years ago

Correct, useParams is only for the ancestor routes, not the routes underneath that component.