use-query-params: ReactRouterRoute breaks with React Router v6

This library no longer works with React Router v6 (currently in alpha).

Warning: Failed prop type: Invalid prop `children` supplied to `Route`, expected a ReactNode.
    in Route (created by QueryParamProvider)

React Router v6 no longer allows for functions as child components. The following code from this library breaks:

https://github.com/pbeshai/use-query-params/blob/32d79f2a47257723fa8459047b2eab1898a61c59/src/QueryParamProvider.tsx#L183-L194

The new signature of Route in React Router v6 is:

export interface RouteProps {
  caseSensitive?: boolean;
  children?: React.ReactNode;
  element?: React.ReactElement | null;
  path?: string;
}

https://github.com/ReactTraining/react-router/blob/dev/packages/react-router/index.tsx#L235-L240

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 16
  • Comments: 21 (4 by maintainers)

Most upvoted comments

Hi all, finally took some time to see what was needed to get this working. @noah-potter’s suggestion might work too, I didn’t investigate, but an alternative style is available in the examples directory.

https://github.com/pbeshai/use-query-params/tree/master/examples/react-router-6

Update See a TypeScript version here

Perhaps after release I’ll look towards building it into the library, but for now it should give you an idea. The basics repeated here:

import * as React from 'react';
import {
  BrowserRouter,
  Routes,
  Route,
  useNavigate,
  useLocation,
} from 'react-router-dom';
import {
  NumberParam,
  QueryParamProvider,
  useQueryParam,
} from 'use-query-params';

const App = ({}) => {
  return (
    <BrowserRouter>
      {/* adapt for react-router v6 */}
      <QueryParamProvider ReactRouterRoute={RouteAdapter}>
        <Routes>
          <Route path="/" element={<Home />} />
        </Routes>
      </QueryParamProvider>
    </BrowserRouter>
  );
};

/**
 * This is the main thing you need to use to adapt the react-router v6
 * API to what use-query-params expects.
 *
 * Pass this as the `ReactRouterRoute` prop to QueryParamProvider.
 */
const RouteAdapter = ({ children }) => {
  const navigate = useNavigate();
  const location = useLocation();

  const adaptedHistory = React.useMemo(
    () => ({
      replace(location) {
        navigate(location, { replace: true, state: location.state });
      },
      push(location) {
        navigate(location, { replace: false, state: location.state });
      },
    }),
    [navigate]
  );
  return children({ history: adaptedHistory, location });
};

v6 has been released now, any chance of integrating the above into the this library?

I ran into some weird bug using this workaround solution with RouteAdapter:

Navigating to a different location causes the inside of the adapted history to be replaced with the stale path and prevents navigation. On the second try, navigation works fine.

It seems that navigation inside adaptedHistory doesn’t need to pass the entire location, so I changed the implementation with only search param passing

const RouteV6Adapter: FC<{ children?: ReactNode }> = ({ children }) => {
    const navigate = useNavigate();
    const location = useLocation();

    const adaptedHistory = useMemo(
        () => ({
            push: ({ search, state }: Location) => navigate({ search }, { state }),
            replace: ({ search, state }: Location) => navigate({ search }, { replace: true, state }),
        }),
        [navigate],
    );

    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    return children({ history: adaptedHistory, location });
};

@pbeshai what do u think? Maybe better to suggest this solution in examples

Hi there, please give the React Router 6 adapter in v2.0.0-rc.0 a try and let me know if you run into issues. See the changelog for details.

I ended up writing an adapter that transformed react-router’s new useSearchParms to the useQueryParams API and then removed use-query-params from my project.

@pbeshai does this solution not re-render the provider and all its children every time location changes? That is what it was doing for me.

add type definition:

import type { Location } from 'history'

Hi, any updates here to make use-query-params compatible with react-router v6?

so how we can use this with v6 ?

Wowwww it took so many years for this to happen, finally my dreams of them just providing it are coming close to being realized 😭 😁 Thank you for sharing! Perhaps it will be sufficient to combine their useSearchParams hook with the encoders from https://github.com/pbeshai/serialize-query-params to get a similar experience in the meantime.

For what it’s worth, react-router v6 now includes useSearchParams. It’s lower level and not as convenient as this library but could serve as a building block toward adding RR6 compatibility.

If anyone has ideas on how to support it, please feel free to create a PR or share your idea here. I haven’t even looked at RR 6 yet. I’d be ok with adding a different prop to the QueryParamProvider for RR6 specifically if that’s required. Otherwise, I’ll probably look at it if I ever decide to switch to RR 6.