React-router: [v6] Equivalent to <Route render={} /> in v6?

Created on 19 Nov 2020  路  5Comments  路  Source: ReactTraining/react-router

What is the issue?

The main issue is that the render prop in <Route /> component was forcing the content of this route to re-render every time the location has changed but with v6 we struggled to find a way to do the same.
Is there an official way of doing the same what a render prop in <Route /> component was used to do in the old v5 implementation?

Example and suggested solution

Here is how it used to be in v5

<HashRouter>
  <Route render={() => <Bar />} />
  <Switch>
    <Route path="/foo" exact={false} component={Foo} />
    <Route path="/foo/:id" exact={true} component={Foo} />
    <Route path="/foo/:id/:lang" component={Foo} />
    <Redirect to="/foo" />
  </Switch>
</HashRouter>

No matter where you navigate, the <Bar /> component will always re-render.

Here is what we came up in v6 to achieve the same

<HashRouter>
  <Routes>
    <Route path="*" element={<Bar />}>
      <Route path="return" element={<Navigate to="/foo" />} />
      <Route path="foo">
        <Route path="/" element={<Foo />} />
        <Route path=":id" element={<Foo />} />
        <Route path=":id/:lang" element={<Foo />} />
      </Route>
    </Route>
  </Routes>
</HashRouter>

and the<Bar /> component looks like this:

const Bar = () => {
  const {key} = useLocation();
  console.log('==> bar');

  return <Outlet key={key} />;
};

Now this does the same as the v5 example above.

Again the big question...

Is this a valid solution? Is there a better way of doing the same?

Thank you

Most helpful comment

@Bekaxp Solution:

const Bar = () => {
  const {pathname} = useLocation();

  console.log('==> side-effect for', pathname);

  return null;
};

...

<HashRouter>
  <Routes>
    <Route path="return" element={<Navigate to="/foo" />} />
    <Route path="foo">
      <Route path="/" element={<Foo />} />
      <Route path=":id" element={<Foo />} />
      <Route path=":id/:lang" element={<Foo />} />
    </Route>
  </Routes>
  <Bar />
</HashRouter>

Note: see <Bar /> is located outside of the <Routes> and triggers the side-effect in any location change.

All 5 comments

You should be using hooks, rather than trying to get Route to re-render your component. This is better because it will always re-render when the location changes, not just when placed under a Route component.

@timdorr While I didn't post this, I disagree that the hooks are necessarily always better.

If I were to use hooks I would need to either 1. fundamentally couple pages to the routes or 2. create new wrapper components to maintain decoupling. Option 1 is highly undesirable at my work since we use the same pages in multiple apps (i.e. user pages duplicated within the admin app) as a different route. Option 2 seems incredibly verbose and really begs the question whether or not this approach really increases DX or readability. I may be missing something, but the render approach really worked quite well even in the brand new hooks world.

You should be using hooks, rather than trying to get Route to re-render your component. This is better because it will always re-render when the location changes, not just when placed under a Route component.

Can you give us an example how to do it with hooks? I mean to have the exact functionality as the render prop.
As far as I know the hook will only be fired once the path of the functional component matches the location, while the render prop was doing that also if the route was not matching because it knew that there was a location change.
Thanks.

@Bekaxp Solution:

const Bar = () => {
  const {pathname} = useLocation();

  console.log('==> side-effect for', pathname);

  return null;
};

...

<HashRouter>
  <Routes>
    <Route path="return" element={<Navigate to="/foo" />} />
    <Route path="foo">
      <Route path="/" element={<Foo />} />
      <Route path=":id" element={<Foo />} />
      <Route path=":id/:lang" element={<Foo />} />
    </Route>
  </Routes>
  <Bar />
</HashRouter>

Note: see <Bar /> is located outside of the <Routes> and triggers the side-effect in any location change.

@artola Thanks, thats way cleaner.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hgezim picture hgezim  路  3Comments

nicolashery picture nicolashery  路  3Comments

alexyaseen picture alexyaseen  路  3Comments

andrewpillar picture andrewpillar  路  3Comments

ryansobol picture ryansobol  路  3Comments