Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to preserve query parameters in react-router v4

Users redirected to my app after login (server on java), and they have url, which looks like this http://10.8.0.29:8083/html/?locale=RU&token=1c5c71f2-dcda-4a51-8cf6-f8f6ff1031d0&returnTo=http://10.8.0.23:8080/

(with some params, html - is folder where sources located). I need to preserve this params while navigate on my app. So far I didn't found any simple solution to this problem except this How to redirect with react-router while preserving initial query parameters? old question, so I arise this question again, in hope. Thanks in advance.

like image 899
Dmitry Malugin Avatar asked Apr 25 '17 14:04

Dmitry Malugin


People also ask

How do you pass a query parameter in react router?

We can define and use a useQuery Hook in a component to get query parameters. To pass in query parameters, we just add them to the Link s to props as usual. For example, we can write the following: We first defined the useQuery Hook to get the query parameters of the URL via the URLSearchParams constructor.

How do you protect routes in react?

To protect routes, the private components must also have access to the isLoggedIn value. You can do this by creating a new component that accepts the isLoggedIn state as a prop and the private component as a child. For instance, if your new component is named "Protected", you would render a private component like this.

Why is switch keyword used in react router v4?

The switch component looks through all of its child routes and it displays the first one whose path matches the current URL. This component is what we want to use in most cases for most applications, because we have multiple routes and multiple plate pages in our app but we only want to show one page at a time.

What is the BrowserRouter /> component?

BrowserRouter: BrowserRouter is a router implementation that uses the HTML5 history API(pushState, replaceState and the popstate event) to keep your UI in sync with the URL. It is the parent component that is used to store all of the other components.


2 Answers

In React-Router 4.3 (not sure about earlier versions), if you have a <Route> just above, something like this should work: (this is in Typescript)

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

Explanation: You use the render: (props) => .... proprety of the <Route> tag, instead of component: ..., because render gives you props, so then inside the <Redirect> you can use props.location.search and in that way access the current query params, and reuse in the redirect.

If there's no <Route> above, maybe you cannot. I just asked here: How preserve query string and hash fragment, in React-Router 4 <Switch><Redirect>?

like image 20
KajMagnus Avatar answered Sep 20 '22 02:09

KajMagnus


I already shared my solution for react-router v3 in the comment to your question.

Below is my solution for react-router v4.

Use the following createPreserveQueryHistory function to override history.push and history.replace methods so they will preserve specified query parameters:

import queryString from 'query-string';
import {createBrowserHistory} from 'history'

function preserveQueryParameters(history, preserve, location) {
    const currentQuery = queryString.parse(history.location.search);
    if (currentQuery) {
        const preservedQuery = {};
        for (let p of preserve) {
            const v = currentQuery[p];
            if (v) {
                preservedQuery[p] = v;
            }
        }
        if (location.search) {
            Object.assign(preservedQuery, queryString.parse(location.search));
        }
        location.search = queryString.stringify(preservedQuery);
    }
    return location;
}

function createLocationDescriptorObject(location, state) {
    return typeof location === 'string' ? { pathname: location, state } : location;
}

function createPreserveQueryHistory(createHistory, queryParameters) {
    return (options) => {
        const history = createHistory(options);
        const oldPush = history.push, oldReplace = history.replace;
        history.push = (path, state) => oldPush.apply(history, [preserveQueryParameters(history, queryParameters, createLocationDescriptorObject(path, state))]);
        history.replace = (path, state) => oldReplace.apply(history, [preserveQueryParameters(history, queryParameters, createLocationDescriptorObject(path, state))]);
        return history;
    };
}

const history = createPreserveQueryHistory(createBrowserHistory, ['locale', 'token', 'returnTo'])();

Then use it in your router definition:

<Router history={history}>
    ...
</Router>

For those who use TypeScript:

import {History, LocationDescriptor, LocationDescriptorObject} from 'history'
import queryString from 'query-string'
import LocationState = History.LocationState

type CreateHistory<O, H> = (options?: O) => History & H

function preserveQueryParameters(history: History, preserve: string[], location: LocationDescriptorObject): LocationDescriptorObject {
    const currentQuery = queryString.parse(history.location.search)
    if (currentQuery) {
        const preservedQuery: { [key: string]: unknown } = {}
        for (let p of preserve) {
            const v = currentQuery[p]
            if (v) {
                preservedQuery[p] = v
            }
        }
        if (location.search) {
            Object.assign(preservedQuery, queryString.parse(location.search))
        }
        location.search = queryString.stringify(preservedQuery)
    }
    return location
}

function createLocationDescriptorObject(location: LocationDescriptor, state?: LocationState): LocationDescriptorObject {
    return typeof location === 'string' ? {pathname: location, state} : location
}

export function createPreserveQueryHistory<O, H>(createHistory: CreateHistory<O, H>,
                                                 queryParameters: string[]): CreateHistory<O, H> {
    return (options?: O) => {
        const history = createHistory(options)
        const oldPush = history.push, oldReplace = history.replace
        history.push = (path: LocationDescriptor, state?: LocationState) =>
            oldPush.apply(history, [preserveQueryParameters(history, queryParameters, createLocationDescriptorObject(path, state))])
        history.replace = (path: LocationDescriptor, state?: LocationState) =>
            oldReplace.apply(history, [preserveQueryParameters(history, queryParameters, createLocationDescriptorObject(path, state))])
        return history
    }
}
like image 138
mixel Avatar answered Sep 21 '22 02:09

mixel