React-router: [v6] Forced use of useParams and useLocation hooks potentially lead to bad design

Created on 27 May 2020  路  6Comments  路  Source: ReactTraining/react-router

I have had a look at version 6 and I really like most of the changes. It's so much cleaner and more logical than previous versions in many places.

The only thing that really bugs me is (so it seems to me at least) being forced to use the useParams and useLocation hooks to access the parameters and current location. I think this potentially leads to bad application design because components are location-aware where I think they shouldn't.

For example, let's say I have a component <CustomerView customerId="..." />. This component can be used anywhere within the application, can easily be unit-tested and doesn't have to know under which location(s) it is accessible. One could even map an array of customer IDs to an array of CustomerView components etc.

In the previous React Router versions, I simply created a <Route /> with a render prop, something like this:

<Route path="/customers/:customerId" render={props => <CustomerView customerId={props.match.params.customerId} />} ... />

In v6, this is no longer possible. I would have to make the <CustomerView /> component location-aware by using the provided hooks.

stale

Most helpful comment

I agree that it is the best design to separate into two components - one "location-aware" container component and one presentational component. However this is quite verbose, wasn't needed in previous versions and I'm quite sure many won't do it that way.

Perhaps this is something for the documentation. The "quick and dirty" way (for websites etc.) and the "elegant" way.

Anyway, thank you and keep up the great work! :-)

All 6 comments

You can work around it like this:

function ComponentWithParams({ children }) {
  const params = useParams();

  if (typeof children === "function") {
    const ret = children(params);
    return ret || null;
  } else {
    return children;
  }
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path=":id">
          <ComponentWithParams>
            {(params) => (
              <CustomerView customerId={params.id} />
            )}
          </ComponentWithParams>
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

Thank you for the quick response and for the suggestion. This is indeed quite similar to the workaround I have written for my project.

But still I think that the current design - the forced use of useParams and useLocation hooks - leads to potentially bad application design, so what I wanted is ask you to reconsider whether the hooks should be the only obvious way to access the parameters and location.

I think for components that represent a page (which isn't used anywhere else, but is used for an exact location), the hooks can make sense and make the code easier to read.

But for components that are not only used in routes, but can be used anywhere, having to write a wrapper (where it wasn't needed before) feels just as bad as making the component location-aware.

I would say it improves application design: you know have to write a component per (sub-)page, where you access the needed data (container component) and pass it down to your presentational component, that you can use everywhere where you want.

I agree that it is the best design to separate into two components - one "location-aware" container component and one presentational component. However this is quite verbose, wasn't needed in previous versions and I'm quite sure many won't do it that way.

Perhaps this is something for the documentation. The "quick and dirty" way (for websites etc.) and the "elegant" way.

Anyway, thank you and keep up the great work! :-)

I agree that it is the best design to separate into two components - one "location-aware" container component and one presentational component. However this is quite verbose, wasn't needed in previous versions and I'm quite sure many won't do it that way.

Perhaps this is something for the documentation. The "quick and dirty" way (for websites etc.) and the "elegant" way.

Anyway, thank you and keep up the great work! :-)

Absolutely agree. We used to have withRouter, and I was just going to use it to give params to my modal component that, of course, is not rendered with <Route>, and I discovered that it's deprecated in 6. Super sad.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
You can add the fresh label to prevent me from taking any action.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ackvf picture ackvf  路  3Comments

alexyaseen picture alexyaseen  路  3Comments

misterwilliam picture misterwilliam  路  3Comments

ryansobol picture ryansobol  路  3Comments

jzimmek picture jzimmek  路  3Comments