use-query-params icon indicating copy to clipboard operation
use-query-params copied to clipboard

react-router (react-router-dom) v7 support

Open shadoworion opened this issue 1 year ago • 7 comments

Error when moving from "react-router-dom" to "react-router"

✘ [ERROR] Could not resolve "react-router-dom"

    node_modules/use-query-params/adapters/react-router-6/index.js:7:7:
      7 │ } from "react-router-dom";

shadoworion avatar Nov 26 '24 09:11 shadoworion

You can make your own adapter and use that instead of the ReactRouter6Adapter in your QueryParamProvider. I just took the one from here and changed the import.

import { useContext } from 'react';
import {
  UNSAFE_NavigationContext,
  useNavigate,
  useLocation,
  UNSAFE_DataRouterContext,
} from 'react-router'; // changed from react-router-dom

const ReactRouter7Adapter = ({ children }) => {
  // we need the navigator directly so we can access the current version
  // of location in case of multiple updates within a render (e.g. #233)
  // but we will limit our usage of it and have a backup to just use
  // useLocation() output in case of some kind of breaking change we miss.
  // see: https://github.com/remix-run/react-router/blob/f3d87dcc91fbd6fd646064b88b4be52c15114603/packages/react-router-dom/index.tsx#L113-L131
  const { navigator } = useContext(UNSAFE_NavigationContext);
  const navigate = useNavigate();
  const router = useContext(UNSAFE_DataRouterContext)?.router;
  const location = useLocation();

  const adapter = {
    replace(location) {
      navigate(location.search || '?', {
        replace: true,
        state: location.state,
      });
    },
    push(location) {
      navigate(location.search || '?', {
        replace: false,
        state: location.state,
      });
    },
    get location() {
      // be a bit defensive here in case of an unexpected breaking change in React Router
      return router?.state?.location ?? navigator?.location ?? location;
    },
  };

  return children(adapter);
};

Put that in your project somewhere and import it instead of use-query-params/adapters/react-router-6

KevinKingsbury avatar Nov 28 '24 13:11 KevinKingsbury

@KevinKingsbury

Yea, It's working.

Only one thing... I don't know, is it important?

const { navigator } = useContext(UNSAFE_NavigationContext);
...
navigator?.location -> /// Property 'location' does not exist on type 'Navigator'

shadoworion avatar Dec 02 '24 17:12 shadoworion

In the code from my comment, it will coalesce to location coming from the useLocation hook.

KevinKingsbury avatar Dec 02 '24 22:12 KevinKingsbury

Hi all,

I've had success with an adapter as simple as:

import { useLocation, useNavigate } from 'react-router';

export default function ReactRouterAdapter({ children }) {
  const navigate = useNavigate();

  return children({
    location: useLocation(),
    push: location => navigate({ search: location.search }, { state: location.state }),
    replace: location => navigate({ search: location.search }, { replace: true, state: location.state })
  });
}

Without using all the RR internals. My test suite is all green and the app works as expected. Not sure why this was required in the first place...

Cheers, David

ddolcimascolo avatar Dec 25 '24 13:12 ddolcimascolo

Hi all,

I've had success with an adapter as simple as:

import { useLocation, useNavigate } from 'react-router';

export default function ReactRouterAdapter({ children }) {
  const navigate = useNavigate();

  return children({
    location: useLocation(),
    push: location => navigate({ search: location.search }, { state: location.state }),
    replace: location => navigate({ search: location.search }, { replace: true, state: location.state })
  });
}

Without using all the RR internals. My test suite is all green and the app works as expected. Not sure why this was required in the first place...

Cheers, David

is this still working as expected?

punkpeye avatar Jan 11 '25 13:01 punkpeye

Hi all, I've had success with an adapter as simple as:

import { useLocation, useNavigate } from 'react-router';

export default function ReactRouterAdapter({ children }) {
  const navigate = useNavigate();

  return children({
    location: useLocation(),
    push: location => navigate({ search: location.search }, { state: location.state }),
    replace: location => navigate({ search: location.search }, { replace: true, state: location.state })
  });
}

Without using all the RR internals. My test suite is all green and the app works as expected. Not sure why this was required in the first place... Cheers, David

is this still working as expected?

Yes, like a charm 👍

ddolcimascolo avatar Jan 11 '25 15:01 ddolcimascolo

For what it's worth, here's a TypeScript version that compiles:

// ReactRouter7Adapter.ts
import {useLocation, useNavigate} from 'react-router';
import type {
  PartialLocation,
  QueryParamAdapterComponent,
} from 'use-query-params';

export const ReactRouter7Adapter: QueryParamAdapterComponent = ({children}) => {
  const navigate = useNavigate();
  const location = useLocation();

  return children({
    location,
    push: ({search, state}: PartialLocation) => navigate({search}, {state}),
    replace: ({search, state}: PartialLocation) =>
      navigate({search}, {replace: true, state}),
  });
};

j0nas avatar Apr 09 '25 09:04 j0nas