2

I have the below swtich using react-router. When I open a new tab and go to any path it always redirects to /login. If I remove the bottom switch (with no path - the default), it works as expected. If I navigate to any path having already loaded the react app (i.e. from /login), it works as expected.

Why is the switch not detecting the right path apparently only on first load when the nomatch is included?

Edit: for clarity this is using hashRouter on localhost, react-router-dom v5.2

<Switch>
        <div className="loginForm align-middle">
            <img src="./img/logo.svg" alt="Restocker logo" className="w-100 p-3 mb-4"/>
            <Route path="/verify">
                <Verify/>
            </Route>
            <Route path="/reVerify">
                <ReVerify/>
            </Route>
            <Route path="/register">
                <Register/>
            </Route>
            <Route path="/forgotPassword">
                <ForgotPassword/>
            </Route>
            <Route path="/resetPassword">
                <Verify type={"password"}/>
            </Route>
            <Route path="/login">
                <Login/>
            </Route>
            <Route> //same result using path="*"
                //no match - default to login
                <Redirect to="/login"/>
            </Route>
        </div>
    </Switch>

Similar topics don't seem to apply to this scenario - either refer to not matching at all (mine does once loaded) or are from much older (2015) versions.

Laurence Summers
  • 201
  • 1
  • 3
  • 14
  • 1
    The `Switch` component should directly wrap the `Route` components if you want it to work correctly. With the `Switch` *outside* the wrapper `div` all the inner routes are inclusively matched and rendered, so the generic route rendering the `Redirect` is always matched and rendered. What is the URL/path when the routing isn't working versus when it does? – Drew Reese Dec 30 '21 at 18:41
  • 1
    @DrewReese sorry I missed your comment but through trial and error came to the same conclusion - you were completely right is was the indirect wrapping which was causing the behaviour - thanks! – Laurence Summers Dec 31 '21 at 19:06

2 Answers2

0

It's important to note that the current version of react-router-dom v6 doesn't support the <switch> component instead it uses the <BrowserRouter> which I like to import as <Router>

import {
  BrowserRouter as Router,
  Route,
  Routes,
} from "react-router-dom";

Also, the v6 uses the <element> component to call the component you wish to run on a particular route.

<Router>
    <Navbar/>
    <Routes>
      <Route path="/" element={<Home />} />
       <Route path="about" element={<AboutPage />} />
       <Route path="contact" element={<ContactPage />} />
      </Routes>
    </Router>

which means you will have to import those components as well.

import Navbar from "./components/NavBar";
import Home from "./pages";
import AboutPage from "./pages/aboutPage";
import ContactPage from "./pages/contactPage";

I've included a Navbar component and placed it outside inside the <Router> but outside the <Routes> in order for the nav to appear on all routes. But that's more of a design choice.

You might also run into a problem where your route will open and page at the same area on the screen where you were when clicked on the route. Let me know if you'll need help with that. You might not need it if you're app is no longer than the full page length

Michael Khaikin
  • 103
  • 1
  • 8
  • 1
    What does `react-router-dom` v6 have to do with OP's question? They are using a `Switch` component so they are clearly using an older version. – Drew Reese Dec 30 '21 at 18:59
  • Thanks for your answer, but I'm on v5 (didn't even know v6 was out!), I've selected only the switch code but it is wrapped in a `` higher in the tree - like I said it works fine *except* on first load *only* when the `noMatch` route is included – Laurence Summers Dec 30 '21 at 19:06
  • in my opinion, you'd probably be better of updating to V6. there are a bunch of new useful features like restricted routes and much more – Michael Khaikin Dec 30 '21 at 19:12
  • 1
    If they were using RRDv6 and still trying to use a `Switch` there'd be errors/warnings. Since there wasn't mention of these and the app is rendering then it's a safe assumption that v6 isn't being used. – Drew Reese Dec 30 '21 at 19:12
  • @MichaelKhaikin I understand your point, but no errors are being thrown - the setup works correctly except in this very particular scenario and I'm trying to understand why. Upgrading to v6 wouldn't really answer my question, and if swtiches are no longer supported that suggests a fair bit of work to migrate – Laurence Summers Dec 30 '21 at 19:19
  • @LaurenceSummers We recommend waiting for the backwards compatibility package to be released before upgrading apps that have more than a few routes. Straight from the site i guess you'll just have to wait – Michael Khaikin Dec 30 '21 at 19:39
0

I thought it would be something silly - the containing div should be outside the switch. It now works as expected (I also changed to using component props instead of children, but both seem to work once the div has been moved).

Working code:

    <div className="loginForm align-middle">
        <img src="./img/logo.svg" alt="Restocker logo" className="w-100 p-3 mb-4"/>
        <Switch>
            <Route path="/verify" component={Verify}/>
            <Route path="/reVerify" component={ReVerify}/>
            <Route path="/register" component={Register}/>
            <Route path="/forgotPassword" component={ForgotPassword}/>
            <Route path="/resetPassword" render={() => Verify("password")}/>
            <Route path="/login" component={Login}/>
            <Redirect to="/login"/>
        </Switch>
    </div>

Note that now all direct children of Switch are now only Route

Edit tweaked based on advice of Drew Reese

Laurence Summers
  • 201
  • 1
  • 3
  • 14
  • 1
    The `"/resetPassword"` route should use the `render` prop if you are wanting to pass the `type` prop through, i.e. ` } />`. Also, the `Redirect` doesn't need to be wrapped in a generic `Route`, you can directly render `Redirect` in the `Switch`. – Drew Reese Jan 01 '22 at 20:26
  • @DrewReese you're right, thanks for the advice! – Laurence Summers Jan 15 '22 at 09:28