Simon
Simon

Reputation: 389

react-router V6 "url changes but component does not render"

Despite checking all the answers already posted on stack overflow I coudn't find the solution for my case, so I'll post it.

I'm using {Router} from 'react-router (v6) to be able to use a custom history object to be able to navigate in my redux actions.

Here are my files:

index.tsx

import { Router } from 'react-router';
import myHistory from './utils/history';

ReactDOM.render(
   <Provider store={store}>
      <Router navigator={myHistory} location={myHistory.location}>
         <App />
      </Router>
   </Provider>,
   document.getElementById('root')
);

App.tsx

import MainNavigation from './components/navigations/MainNavigation';
import Routes from './routes/Routes';

const App = () => {
   return (
      <>
         <MainNavigation />
         <Routes />
      </>
   );
};

export default App;

MainNavigation.tsx

import { Link } from 'react-router-dom';

const MainNavigation = () => {
   return (
      <nav className='navigation'>
         <ul>
            <li>
               <Link to='/'>Home</Link>
            </li>
            <li>
               <Link to='/contact'>Contact</Link>
            </li>

            <li>
               <Link to='/about'>A propos</Link>
            </li>

            <li>
               <Link to='/user-profile'>Votre compte</Link>
            </li>
         </ul>
      </nav>
   );
};

export default MainNavigation;

Route.ts

import { Route, Routes as Routing } from 'react-router';

const Routes = () => {
   return (
      <Routing>
         <Route path='/' element={<Home />} />
         <Route path='/about' element={<About />} />
         <Route path='/contact' element={<ContactForm />} />
         <Route path='/user-profile' element={<UserAccount />} />
         {/* <Route path='*' component={Error404} /> */}
         {/* <Navigate to='/' /> */}
      </Routing>
   );
};

export default Routes;

The issue: when I click on one of the link the url changes to the corresponding path but the component doesn't render. I have to refresh the page to actually make it appear. I really don't know what's going on.

Thank you.

Upvotes: 6

Views: 8898

Answers (3)

Drew Reese
Drew Reese

Reputation: 202605

From what I can tell you've not correctly implemented your Router component.

Router

If you examine one of the higher-level routers, like BrowserRouter you'll notice they internalize the history object and also store the current location from the result of a navigating action (history.listen).

export function BrowserRouter({
  basename,
  children,
  window
}: BrowserRouterProps) {
  let historyRef = React.useRef<BrowserHistory>();
  if (historyRef.current == null) {
    historyRef.current = createBrowserHistory({ window });
  }

  let history = historyRef.current;
  let [state, setState] = React.useState({
    action: history.action,
    location: history.location
  });

  React.useLayoutEffect(() => history.listen(setState), [history]);

  return (
    <Router
      basename={basename}
      children={children}
      location={state.location}
      navigationType={state.action}
      navigator={history}
    />
  );
}

Create a CustomRouter that consumes a custom history object and manages the state:

const CustomRouter = ({ history, ...props }) => {
  const [state, setState] = useState({
    action: history.action,
    location: history.location
  });

  useLayoutEffect(() => history.listen(setState), [history]);

  return (
    <Router
      {...props}
      location={state.location}
      navigationType={state.action}
      navigator={history}
    />
  );
};

Import and use your new custom router instead.

import { CustomRouter } from './components/router';
import myHistory from './utils/history';

ReactDOM.render(
  <Provider store={store}>
    <Router history={myHistory}>
      <App />
    </Router>
  </Provider>,
  document.getElementById('root')
);

Upvotes: 4

Mark Barton
Mark Barton

Reputation: 897

you need to include 'index' if you wish to use the '/' route.

<Route path='/about' index element={<About />} />

Also, React Router v6 is radically different to v5. it switches to the new Outlet based routing. In order to display your pages, you need to have an Outlet in your registered Route component. For example, in the app.js component, you need to add <Outlet /> in order to render these pages.

I like to think of the new routing as Container-based navigation. So create a component called AboutContainer. Include <Outlet /> in this component.

export const AboutContainer = () => (
  <Outlet />
)

Then, add a component called AboutPage and put your about page logic inside. In your Routes, nest this About page as a new route inside your AboutContainer Router logic;

<Routes>
  <Route path='/about' element={<AboutContainer />}>
    <Route path='/' index element={<AboutPage />} />
  </Route>
...
</Routes>

This means that every time you navigate to /about, you are shown the AboutPage component. Take it a step further and add an additional route

<Routes>
  <Route path='/about' element={<AboutContainer />}>
    <Route path='/' index element={<AboutPage />} />
    <Route path='/you' index element={<AboutYouPage />} />
  </Route>
...
</Routes>

This is how routing is now handled in React Router 6

Upvotes: 1

acbay
acbay

Reputation: 892

You can use your routes inherited way like;

<Routes>
  <Route path="/">
    <Route index element={<Home />} />
    <Route path="about" element={<About />} />
    <Route path="contact" element={<ContactForm />} />
    <Route path="user-profile" element={<UserAccount />} />
    <Route path="*" element={<NoMatch />} />
  </Route>
</Routes>

Upvotes: 1

Related Questions