'How preserve query string and hash fragment, in React-Router 4 <Switch><Redirect>?

If you have a <Redirect> inside a <Route>, you'll get a location and can do: <Redirect search={props.location.search} hash={props.location.hash} ....

However, when directly inside a top level <Switch>, the <Redirect> doesn't have any props.location to access search and hash on.

Is there no way to preserve the query string and hash fragment, in a <Redirect> directly after a top level <Switch> (no <Route> higher up in the tree)?

Example: (Typescript)

Router({},
  Switch({},
    Redirect({ path: '/', to: '/latest', exact: true }))))

Changing to: '/latest' to to: { pathname:... search:.. hash:.. } doesn't work because there's no props.location available to access the original .search and .hash on.

Here (https://github.com/ReactTraining/react-router/issues/5818#issuecomment-384934176 ) the devs says the preserve-query-and-hash problem has been solved but I cannot find anything that works in the above case:

> any option the save query string on redirect?

> it will be implemented in 4.3.0. See release notes here: https://github.com/ReactTraining/react-router/releases/tag/v4.3.0-rc.1

Those release notes links to: https://github.com/ReactTraining/react-router/pull/5209 which doesn't mention anything that seems to work.

Maybe they meant only for <Redirect> inside a <Route> already? Then, one can do something like:

Route({ path: ..., render: (props) => function() {
  Redirect({ to: { pathname:... search: props.location.search, ... } ...}));


Solution 1:[1]

Until the <Redirect /> component gets its own history subscriber, you can make your own:

const RouteAwareRedirect = props => (
  <Route render={routeProps => 
    <Redirect to={props.to(routeProps)} />
  }/>
)

Then use that where ever you want:

<RouteAwareRedirect to={({ location }) => ({ 
  // use location.pathname, etc .. 
}) />

Solution 2:[2]

If there's no other way (apparently there isn't, see Azium's answer) ... then this works :- ) at least with exact and strict both true (haven't tested other combos).

Use like so: (and it'll change the path only, not query string or hash)

RedirPath({ path: '/', to: '/latest', exact: true })

and works in a <Switch> with no <Route> above. There's a <Route> inside instead :- P You need to remove dieIf.

License: MIT. (Not CC0.)

/**
 * Redirects the URL path only — preserves query string and hash fragment.
 */
export function RedirPath(props: { path: string, to: string, exact: boolean, strict?: boolean }) {
  // @ifdef DEBUG
  dieIf(props.to.indexOf('?') >= 0, 'TyE2ABKS0');
  dieIf(props.to.indexOf('#') >= 0, 'TyE5BKRP2');
  // @endif
  const path = props.path;
  const exact = props.exact;
  const strict = props.strict;
  return Route({ path, exact, strict, render: (routeProps) => {
    return Redirect({
      from: path, exact, strict,
      to: {
        pathname: props.to,
        search: routeProps.location.search,
        hash: routeProps.location.hash }});
  }});
}

Solution 3:[3]

You can extend <Redirect /> like so:

const UtmFriendlyRedirect = props => (
    <Redirect computedMatch={props.computedMatch}
              to={{pathname: props.to, search: props.location.search}}/>
)

And then use it the same way as a normal redirect:

<UtmFriendlyRedirect from='/:articleId' to='/article/:articleId'/>

Solution 4:[4]

In my very limited testing this preserves all search params.

const RedirectAndPreserveSearch = ({ to, ...additionalProps }) => (
  <Redirect
    {...additionalProps}
    to={{
      pathname: to,
      search: new URL(window.location.href).search,
    }}
  />
);

I think this only works as long as nothing else will be interfering with the search params (I'm not sure if that would update the component? It might redirect with the search params from when it mounted?)

I haven't tried it, but you might be able to do something like this with the hash as well.

Solution 5:[5]

A little late to the party but in React-Router 5, it is possible to do it like so:

<Redirect
  from="/"
  to={{
    pathname: "/latest",
    search: window.location.search,
    hash: window.location.hash,
  }}
/>

Hope this helps someone else!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 azium
Solution 2
Solution 3 Clay
Solution 4 Pend
Solution 5 alengel