11

In my react application I have a few parameters that the user comes to the application that provide some information about where they came from. Is there a way using react-router to preserve these query params throughout the entire application. meaning every time a route is changed Id like those query params to stay in the url. The only examples I've seen are passing query params between routes but not keeping them around for every route.

4
  • Did you ever find a solution for this? Commented Dec 13, 2016 at 20:33
  • 2
    Kind of. I intercepted history.listen and added a check to see if the parameters are there or not. If they aren't I added them from redux. It works ok for now but I'd to like make something built into react-router directly. Commented Dec 13, 2016 at 20:40
  • is it works for react router v4? Commented Apr 25, 2017 at 13:21
  • 1
    @Josh see my answer below. Commented May 18, 2017 at 21:12

2 Answers 2

2

UPDATE

Solution for react-router v4 is available.

Solution for react-router v3:

I wrote this little history v3 (it's compatible with react-router v3) enhancer in Typescript. It will preserve given set of query parameters. Be careful - history passed to this function has to be enhanced with useQueries.

import {History, Location, LocationDescriptor} from 'history'

export default function preserveQueryHistory(history: History, queryParameters: string[]): History {
    function preserveQueryParameters(path: LocationDescriptor): Location {
        const location = history.createLocation(path)
        const currentQuery = history.getCurrentLocation().query
        for (let p of queryParameters) {
            const v = (currentQuery as any)[p]
            if (v) {
                location.query[p] = v
            }
        }
        return location
    }
    return {
        ...history,
        push(path: LocationDescriptor) {
            history.push(preserveQueryParameters(path))
        },
        replace(path: LocationDescriptor) {
            history.replace(preserveQueryParameters(path))
        }
    }
}

Now use it to create history:

import useQueries from 'history/lib/useQueries'
import createBrowserHistory from 'history/lib/createBrowserHistory'
import preserveQueryHistory from './preserveQueryHistory'

const history = preserveQueryHistory(useQueries(createBrowserHistory)(), ['language'])

And in react-router:

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

More ultimate solution with CreateHistory enhancer function that embeds applying useQueries enhancer and provides ability to inject custom History enhancer:

import {CreateHistory, History, HistoryOptions, HistoryQueries, Location, LocationDescriptor} from 'history'
import useQueries from 'history/lib/useQueries'

function preserveQueryParameters(history: History, queryParameters: string[], path: LocationDescriptor): Location {
    const location = history.createLocation(path)
    const currentQuery = history.getCurrentLocation().query
    for (let p of queryParameters) {
        const v = (currentQuery as any)[p]
        if (v) {
            location.query[p] = v
        }
    }
    return location
}

function enhanceHistory<H>(history: History & H, queryParameters: string[]): History & H {
    return Object.assign({}, history, {
        push(path: LocationDescriptor) {
            history.push(preserveQueryParameters(history, queryParameters, path))
        },
        replace(path: LocationDescriptor) {
            history.replace(preserveQueryParameters(history, queryParameters, path))
        }
    })
}

export function createPreserveQueryHistoryWithEnhancer<O, H, H2>(createHistory: CreateHistory<O, H>,
    queryParameters: string[], enhancer: (h: History) => History & H2): CreateHistory<O, H & H2 & HistoryQueries> {
    return function (options?: HistoryOptions & O): History & H & H2 & HistoryQueries {
        let h = enhancer(useQueries(createHistory)(options)) as History & H & H2 & HistoryQueries
        return enhanceHistory<H & H2 & HistoryQueries>(h, queryParameters)
    }
}

export function createPreserveQueryHistory<O, H>(createHistory: CreateHistory<O, H>,
    queryParameters: string[]): CreateHistory<O, H & HistoryQueries> {
    return createPreserveQueryHistoryWithEnhancer<O, H, {}>(createHistory, queryParameters, h => h)
}

export function preserveQueryHistoryWithEnhancer<H, H2>(history: History & H, queryParameters: string[],
    enhancer: (h: History) => History & H2): History & HistoryQueries & H & H2 {
    return createPreserveQueryHistoryWithEnhancer(
        function () {
            return history
        },
        queryParameters, enhancer)()
}

export function preserveQueryHistory<H>(history: History & H, queryParameters: string[]): History & HistoryQueries & H {
    return preserveQueryHistoryWithEnhancer<H, {}>(history, queryParameters, h => h)
}

Usage with syncHistoryWithStore react-router-redux v4 History enhancer:

import createBrowserHistory from 'history/lib/createBrowserHistory'
import {createPreserveQueryHistoryWithEnhancer} from './preserveQueryHistory'
import {syncHistoryWithStore} from 'react-router-redux'

const store = ...

const history = createPreserveQueryHistoryWithEnhancer(createBrowserHistory, ['language'], function (h: History) {
    return syncHistoryWithStore(h, store)
})()
Sign up to request clarification or add additional context in comments.

1 Comment

cool solution. definitely further than I would've ventured to go writing my own history module.
-4

In this case you can use a Store that holds URL state and you can use browserHistory to push custom history state.

https://github.com/reactjs/react-router/blob/master/docs/guides/Histories.md#browserhistory

import { browserHistory } from 'react-router'

browserHistory.push({});

Here is a link how to navigate outside components

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.