Arman Ebrahimi
Arman Ebrahimi

Reputation: 2297

What is the reason routing in my project not works correctly?

When I type "/create" after the address, I expect it shows Home component. Since I don't use from exact prop. Because path='/c' matches with "/create", but it shows Create component. It first worked fine and when I used from exact prop it worked fine as expected. I don't know why it does not work as expected after I removed exact from the first Route.

It works fine only if I do: <Route path="/" element={<Home />} />

Note: I used this for run server: npx json-server --watch data/db.json --port 8000

See that on Codesandbox: https://codesandbox.io/s/epic-feather-uz2iur

It's the section of my Routes:

function App() {
  return (
    <Router>
      <div className="App">
        <Navbar />
        <div className="content">
          <Routes>
            <Route path="/c" element={<Home />} />
            <Route path="/create" element={<Create />} />
          </Routes>
        </div>
      </div>
    </Router>
  );
}

It gives the same result on my Webstorm(react-router-dom version 5):

  <Router>
      <div className="App">
          <Navbar />
          <div className="content">
              <Switch>
                  <Route path="/">
                      <Home />
                  </Route>
                  <Route path="/create">
                      <Create />
                  </Route>
              </Switch>
          </div>
      </div>
  </Router>

Upvotes: 1

Views: 105

Answers (3)

Drew Reese
Drew Reese

Reputation: 202605

It seems the issue is a misunderstanding of how route path matching changed from react-router@5 to react-router@6.

In RRDv5 the route path was more of a "path prefix", meaning it behaved like you described. A path="/c" could match any URL path starting with "/c". This had its peculiarities when trying to specifically, or exactly, match certain routes. This is often why naive devs would pepper all their routes with the exact prop (because not understanding how matching worked).

For example, your v5 version, it would need to use the exact prop in order to match "/c" exactly and render the Home component, or not match exactly so the Create component could be matched.

<Router>
  <div className="App">
    <Navbar />
    <div className="content">
      <Switch>
        <Route exact path="/c" component={Home} />
        <Route exact path="/create" component={Create} />
      </Switch>
    </div>
  </div>
</Router>

This is because of (A) the way route matching works in v5, and (B) the way the Switch component renders only the first matching Route or Redirect component. The route path order and specificity matters! In almost 100% of the use cases you don't need the exact prop by simply ordering your routes correctly, from most specific to least specific.

The same v5 routes and switch, but ordered correctly:

<Router>
  <div className="App">
    <Navbar />
    <div className="content">
      <Switch>
        <Route path="/create" component={Create} />
        <Route path="/c" component={Home} />
      </Switch>
    </div>
  </div>
</Router>

Note now that "/c" can't match to path="/create" and matching continues down until it finds and matches path="/c".

In RRDv6 paths are now always exactly matched, so the exact prop no longer exists as it's not needed. Each route uses a path ranking system, see Ranking Routes. The specificity of the path yields a rank value and this is used to match a route. Route order is now irrelevant.

<Routes>
  {/* match & render when path is exactly "/c" */}
  <Route path="/c" element={<Home />} />

  {/* match & render when path is exactly "/create" */}
  <Route path="/create" element={<Create />} />
</Routes>

Upvotes: 2

Milos Pavlovic
Milos Pavlovic

Reputation: 1450

There was a breaking change in v6 regarding "exact" prop - it does not exist anymore and all paths are by default exact.

You can read more here: upgrading to v6

Route exact is gone. Instead, routes with descendant routes (defined in other components) use a trailing * in their path to indicate they match deeply

Upvotes: 1

Apostolos
Apostolos

Reputation: 10463

Using react-router-dom version 5 you can use Switch along with * and it should work

<Router>
  <div className="App">
    <Navbar />
    <div className="content"></div>
    <Switch>
      <Route path="/c*">
        <Home />
      </Route>
      <Route path="/create">
        <Create />
      </Route>
    </Switch>
  </div>
</Router>

According to documentation

A Switch looks through all its children elements and renders the first one whose path matches the current URL. Use a any time you have multiple routes, but you want only one of them to render at a time

Working example

Upvotes: 1

Related Questions