I have a web page with a search panel. Search panel has several input fields: id, size, ...

What I want is when user set search values (for example: id=123 and size=45) and press a search button:

  1. searchState in Redux reducer should be updated with new search values (id=123 and size=45)
  2. URL should be changed to "http://mydomain/home?id=123&size=45"

And on the other hand if the user changes URL to http://mydomain/home?id=111&size=22 then:

  1. searchState in reducer should be changed with new search values (id=111 and size=22)
  2. UI search panel inputs should be updated with new values (id=111 and size=22)

How to reach with goal? What router should I use (react-router, redux-router, react-router-redux ot other)?


Solution 1:

I would suggest just using React Router directly and not keeping searchState in Redux. React Router will inject URL parameters into your components, and you can use them in mapStateToProps(state, ownProps) to calculate the final props.

If you really want to see route changes as actions, you can use react-router-redux for two-way syncing, but it won’t give you the params in the state—just the current location. The only use case for it if you want to record and replay actions, and have the URL bar update as you replay them.

It is by no means required—in most cases, just using React Router directly is enough.

Solution 2:

In short: You could use redux-query-sync to keep the URL parameters and fields in the store in sync. It works without any Router.


Longer story: Struggling with the same question, I first appreciated Dan Abramov's answer, suggesting to (in my words) consider the URL as a 'second store', a part of the application state outside the Redux store. But then I found that having two kinds of 'stores' makes it difficult to modify code, to e.g. move things from one to the other, because each 'store' has a different interface. As a developer, I would rather like to select any of the fields in my Redux state, and expose them in the URL, as if the location were a small window to (a part of) my state.

Hence I just published redux-query-sync, to let you provide a selector function for selecting a value from the state, which is then exposed in the window location at a parameter name you specify. To also let it sync in the other direction, thus from the URL to Redux state (e.g. when the application initially loads), you can provide an action creator that will be passed the parameter value, so your reducer can update the state accordingly.

Solution 3:

I also recently finished working on this feature for my company's app. We wanted to add different search queries to the URL.

A few things I would like to share here:

1) We used Redux store and react-router. Since we also used Typescript, we ended up using RouteComponentProps from react-router to use this.props.history.push() to update the URL.

2) We kept all the search queries in the Redux store. The work flow for updating the Redux store and then the URL is as follows:

Select some filtering options in the app => Dispatch actions to update filtering state in the Redux store => update the URL

3) At the end, we also want users to be able to enter an URL with query params and it will update all the filters in the app. For this to work, the work flow is even simpler:

User enters the URL => dispatch actions to update Redux state with query params from the URL. The update of Redux state will automatically cause a re-rendering in the app and all filters will be updated.

So the most important thing here is always keep the Redux state and URL in sync with each other.

Solution 4:

Here's the way I've been handling the first scenario:

  • When the input value changes, its component triggers a callback that was passed as a prop from its container.

  • In the callback, the container dispatches the action responsible for updating Redux state when the event occurs.

  • In the line immediately following the action call, I use this.context.router.push() and pass it the url with the correct query string.

I'm not certain that this is the correct approach. I found it preferable to updating the URL first because, in my opinion, the query string should be a reflection of state rather than the master of it.

Regarding the reverse scenario, I'm really not sure. It seems like manually setting the URL would trigger a full reload, but I might be mistaken.

As for which router to use, I am using React Router by itself. I wasn't really finding value in using the others and this discussion was the clincher for me.