30

I am using the next version of React Router, and it seems to be dropping params. I expect the redirect below to retain the value of channelId, but the to route uses the literal string ":channelId" in the path instead.

<Switch>
  <Route exact path="/" component={Landing} />
  <Route path="/channels/:channelId/modes/:modeId" component={Window} />
  <Redirect
    from="/channels/:channelId"
    to="/channels/:channelId/modes/window" />
</Switch>

This looks like a resolved issue, but it's not working. Is there something else I need to pass to the to route?

JordanM
  • 3
  • 2
Matt Norris
  • 8,596
  • 14
  • 59
  • 90
  • Did you find the solution, Matt? – Sebastian Roth May 25 '17 at 09:31
  • @SebastianRoth I never did, unfortunately. I'm doing things a different way now, using redirect within the component itself. However, it would be great to get this verified because it should work as advertised in the thread linked above. – Matt Norris May 30 '17 at 18:38
  • 1
    FWIW, I've asked this question in the react-router Discord channel. I ended up doing something similar, with a route that has a render method that returns a Redirect with values pulled from props. – Gabriel Isenberg May 30 '17 at 21:02
  • 1
    I don't think the link you provided is to the same issue. That OP is looking to add extra params in addition to the ones :defined in from/to. I'm having the same situation where :someId is coming in as 1234, but getting redirected to a literal ":someID" string in the URL. Very frustrating. – coblr Nov 17 '17 at 17:32

8 Answers8

26

Here's what I've been using, similar to the other answer but without a dependency:

<Route
  exact
  path="/:id"
  render={props => (
    <Redirect to={`foo/${props.match.params.id}/bar`} />;
  )}
/>
Jason D
  • 867
  • 1
  • 12
  • 19
  • this definitely works in `react-router-dom@4.2.2`. weird that the `Redirect` has to be forced to do this this way – worc Jun 06 '18 at 22:33
  • I get `/foo/undefined/bar` in version ` react-router-dom": "^4.2.2` – Badrush Jan 07 '19 at 20:29
  • 1
    This works perfectly. Note that you need to add a slash before `/foo` so it will be an absolute path. – Elron Aug 05 '21 at 23:00
12

I found no such logic in React Router 4 sources, so write own workaround:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import pathToRegexp from 'path-to-regexp';
import { Route, Redirect } from 'react-router-dom';

class RedirectWithParams extends Component {
  render() {
    const { exact, from } = this.props;    
    return (
      <Route
        exact={exact}
        path={from}
        component={this.getRedirectComponent}
      />);
  }

  getRedirectComponent = ({ match: { params } }) => {
    const { push, to } = this.props;
    const pathTo = pathToRegexp.compile(to);
    return <Redirect to={pathTo(params)} push={push} />
  }
};

RedirectWithParams.propTypes = {
  exact: PropTypes.bool,
  from: PropTypes.string,
  to: PropTypes.string.isRequired,
  push: PropTypes.bool
};

export default RedirectWithParams;

usage example:

<Switch>
   <RedirectWithParams
      exact from={'/resuorce/:id/section'}
      to={'/otherResuorce/:id/section'}
   />
</Switch>
Ralph N
  • 4,240
  • 8
  • 28
  • 36
Gleb Lobastov
  • 161
  • 1
  • 4
  • 1
    Looks almost the same as your code: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/generatePath.js – coblr Nov 17 '17 at 17:39
7

You can do this:

<Switch>
  <Route exact path="/" component={Landing} />
  <Route path="/channels/:channelId/modes/:modeId" component={Window} />
  <Route
    exact
    path="/channels/:channelId"
    render={({ match }) => (
      <Redirect to={`/channels/${match.params.channelId}/modes/window`} />   
    )}
  />
</Switch>
Fábio Santos
  • 533
  • 1
  • 5
  • 11
5

I did this, and it worked:

<switch>
  <Route path={`/anypath/:id`} component={Anycomponent} />
   <Route
      exact
      path="/requestedpath/:id"
      render={({ match }) => {
        if (!Auth.loggedIn()) {
          return <Redirect to={`/signin`} />;
        } else {
          return <Redirect to={`/anypath/${match.params.id}`} />;
        }
      }}
    />
</switch>
Emmanuel Ndukwe
  • 326
  • 5
  • 7
4

This functionality has been added to React Router 4 as of 4.3.0. If you're locked into a version before 4.3.x, Gleb's answer is the way to go.

Mike
  • 3,331
  • 2
  • 18
  • 22
4

You can do it using generatePath:

import { Switch, Route, Redirect, generatePath } from "react-router";

<Switch>
  <Route component={PageOne} path="/one" />
  <Route component={PageTwo} path="/two/:id" />
  <Route
    path="/three/:id"
    render={props => (
      <Redirect
        to={generatePath("/two/:id", {
          id: props.match.params.id,
        })}
      />
    )}
  />
  <Route component={NotFoundPage} path="*" />
</Switch>
tdy
  • 36,675
  • 19
  • 86
  • 83
AndreFeijo
  • 10,044
  • 7
  • 34
  • 64
1

In React Router v6+ you could create your own NavigateWithParams like so

import { Navigate, NavigateProps, generatePath, useParams } from 'react-router-dom';

interface Props extends NavigateProps {
  to: string;
}

const NavigateWithParams: React.FC<Props> = ({ to, ...props }) => {
  const params = useParams();

  return <Navigate {...props} to={generatePath(to, params)} />;
};

export default NavigateWithParams;

and use it like so:

  <Route path="/old/:param" element={<NavigateWithParams to="/new/:param" />} />
kernel
  • 3,654
  • 3
  • 25
  • 33
0

Short general version using generatePath that worked for me (redirecting from from path to to path, both having the same parameters):

import { Route, Redirect, generatePath } from "react-router"

<Route path={from} 
       exact
       component={({match}) => 
             <Redirect to={generatePath(to, match.params)} />} />
blutogin
  • 13
  • 5