prolific
prolific

Reputation: 775

React Router v4 - Redirect to same route with different query params

Scenario:

There is a homepage from where on searching user is redirected to a search page with a query param (eg q=abc). Now being on search page, user again tries to search for a different value so he is again redirected to same search page but with a different query param (eg q=xyz)

Problem:

When I am trying to perform a Redirect from Search page again to the same route with a different query param, I am getting an error "You tried to redirect to the same route you're currently on".

Expected Result:

User should be redirected to the same route again with a different query params so that on pressing back button he can go back to the previous query like it would normally be if it hadn't been a SPA. The result should be similar to the case where I had redirected the user from /search/abc to /search/xyz both using the same component.

Possible Solution:

On submitting a search query, I can check if it is the same route and based on that I can either redirect or update the component state some way. Problem with this solution is that user cannot go back to the previous query on clicking browser's back button. I want the page to be added in history.

Code Snippet:

Using below code for redirection in render function inside Search Component which is present on both homepage and search page

if (this.state.isReady) {
        let queryString = "q=" + this.state.q;
        return (
            <Redirect push to={`/search/hotels?${queryString}`} />
        );
    }

I think this is a common problem with any site having a search feature so there should be an elegant way to handle such a situation but I am unable to think about it.

Upvotes: 24

Views: 35360

Answers (7)

Patrick
Patrick

Reputation: 1387

I'm not a fan of other solutions that dip into the history api. I see react-router as providing an abstraction on top of regular browser links/history, so I'd rather change how I'm using that library.

We have a regular Link component:

<Link to='/products/new'/>

and this ^ doesn't need to change. Instead, we changed our Route from this

 <Route path="/products/new" component={Whatever} />

to this:

 <Route path="/products/new" component={Whatever} key={history.location.key} />

and that made it just work. I believe this works because history.location.key is changing whenever the user clicks on a Link component, and that forces the Route to be recreated. So now the component is getting a fresh state like we want.

This is for react-router v4 (yes, it's old...)

Upvotes: 0

Dsneaky8
Dsneaky8

Reputation: 31

Here's my solution using React hooks. Made a new file for these components and import wherever I need it.

import { Route, Link, useLocation, Redirect } from 'react-router-dom';

export const LinkWithQuery = ({ children, to, ...props }) => {
  const { search } = useLocation();

  return (
    <Link to={to + search} {...props}>
      {children}
    </Link>
  );
};

export const RedirectWithQuery = ({ to, exact, from }) => {
  return (
    <Route
      exact={exact}
      path={from}
      render={(props) => <Redirect to={`${to}${props.location.search}`} />}
    />
  );
};

Upvotes: 2

Javier Dottori
Javier Dottori

Reputation: 1330

For react-router-dom v5.1 the to object can include the search field

<Redirect to={{ pathname: "/search/hotels", search: `?${queryString}` }} />

If you manually concatenate those in pathname it doesn't work as expected in my use case. Notice the ? included in the search param.

Also, this to structure has a similar shape to the returned when using the RouterComponentProps.

import { RouteComponentProps } from 'react-router-dom';

interface Props extends RouteComponentProps<{}> {}

export function GoToPrevious(props: Props) {
  // previous page
  let from = (props.history.location.state as any)?.from
  // remember to check undefined's here
  return <Redirect to={from}></>;
}

Upvotes: 0

Hiram Hibbard
Hiram Hibbard

Reputation: 537

The easiest solution I found was just using the location.search prop:

<Redirect from="/color-profile/quiz" to={`/quiz?${props.location.search}`} exact />

Upvotes: 1

Dusty48
Dusty48

Reputation: 618

Ebis answer did not work correctly for me. When adding the query string directly to the pathname, react router tried to match the path, resulting in not founding the route when using the Redirect component.

A workaround was to add a / before the query string like so:

<Redirect to={{ pathname: `/search/hotels/?${queryString}` }} />

However, this didn't seem right. Instead, I've added only the path to the pathname, and the query string to the search key like so:

<Redirect to={{ pathname: '/search/hotels', search: `?${queryString}` }} />

Upvotes: 3

Ebi Igweze
Ebi Igweze

Reputation: 460

Hey you could use the object notation instead of a string literal, and pass a location object containing the new pathname you would like to redirect to example


const Topic = ({ location }) => (
   <Redirect to={{ ...location, pathname: '/topics?user=prolific' }} />
)

so using the spread operator, you retain previous properties of location, and then you can change the pathName to the one you want.

or

You could just format the query for the new route and prepend the route name, before passing it to the Redirect component. e.g.


const Topic = ({ location }) => {
   const userQuery = 'user=prolific&title=The Long Night';
   const newRoute = `topics?${userQuery}`

   return <Redirect to={newRoute} />
}

Hope that helps

Upvotes: 2

prolific
prolific

Reputation: 775

Found A Solution:

Instead of using <Redirect /> from React Router, I used history api to push a new route.

this.props.history.push(`/search/hotels?${queryString}`);

I wrapped my SearchComponent inside withRouter which is a HOC provided by React-Router and then used the history api.

Code Snippet:

import { withRouter } from 'react-router-dom';

class SearchComponent {

    // Component Code goes here

    onSubmit(){
        let queryString = "q=" + this.state.q;
        this.props.history.push(`/search/hotels?${queryString}`);
    }
}

export default withRouter(SearchComponent)

Upvotes: 19

Related Questions