Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sync React Router Params with state

Using React Router Web.

Assume we have a route: /:user?showDetails=true. We know how to get the data from the URL with the useParams hook and something like the useQuery custom hook.

Also, we know how to set this data with history.push(/baruchiro?showDetails=false).

But if we get and set this data, and in case we don't use this to redirect the user from one page to another, but to change the current component (to let the user save its current page view), it's mean that the route is state.

How can I use the route as a state without getting the component dirty with a lot of history.push and useParams?

like image 941
baruchiro Avatar asked Sep 18 '25 12:09

baruchiro


1 Answers

Update

  1. I published this custom hook as npm package: use-route-as-state
  2. This library is outdated because it doesn't support React Router v6.

If you want to use the route as state, you need a way to get the route params, and also update them.

You can't avoid using history.push since this is the way you change your "state", your route. But you can hide this command for cleaner code.

Here is an example of how to hide the get and the update in custom hooks, that make them to looks like a regular useState hook:

To use Query Params as state:

import { useHistory, useLocation} from 'react-router-dom'

const useQueryAsState = () => {
    const { pathname, search } = useLocation()
    const history = useHistory()

    // helper method to create an object from URLSearchParams
    const params = getQueryParamsAsObject(search)

    const updateQuery = (updatedParams) => {
        Object.assign(params, updatedParams)
        // helper method to convert {key1:value,k:v} to '?key1=value&k=v'
        history.replace(pathname + objectToQueryParams(params))
    }

    return [params, updateQuery]
}

To use Route Params as state:

import { generatePath, useHistory, useRouteMatch } from 'react-router-dom'

const useParamsAsState = () => {
    const { path, params } = useRouteMatch()
    const history = useHistory()

    const updateParams = (updatedParams) => {
        Object.assign(params, updatedParams)
        history.push(generatePath(path, params))
    }
    return [params, updateParams]
}

Note to the history.replace in the Query Params code and to the history.push in the Route Params code.

Usage: (Not a real component from my code, sorry if there are compilation issues)

const ExampleComponent = () => {
    const [{ user }, updateParams] = useParamsAsState()
    const [{ showDetails }, updateQuery] = useQueryAsState()

    return <div>
        {user}<br/ >{showDetails === 'true' && 'Some details'}
        <DropDown ... onSelect={(selected) => updateParams({ user: selected }) />
        <Checkbox ... onChange={(isChecked) => updateQuery({ showDetails: isChecked} })} />
    </div>
}
like image 87
baruchiro Avatar answered Sep 20 '25 03:09

baruchiro