2

I'm using React, Redux and React Router. I want to store most of application state in redux store, and some bits of it in URL query. I believe there's two ways of doing that:

  • syncing redux store with URL query after each state change
  • don't store "some bits" in redux store, instead have some way (similar to how reducers work) to change URL query as if it was another store

I'm not sure if first way is even possible, since serialized state length may exceed URL limit. So I probably should go with the second.

For example, I have several checkboxes on a page and I want their state (checked/unchecked) to be mirrored in URL, so I could send my URL link to somebody else and they would have the same checkboxes checked as I do. So I render my checkboxes like that:

export class MyComponent extends React.Component {

    handleCheckboxClick(...) {
        // Some magic function, that puts my urlState into URL query.
        updateUrlState(...)
    }

    render() {
        // 'location' prop is injected by react router.
        const urlState = getUrlState(this.props.location.query);  

        // Let's say 'checkboxes' are stored like so:
        // {
        //     foo: false,
        //     bar: true,
        // }
        const { checkboxes } = urlState;

        return (
            <div>
                { checkboxes.map(
                    (checkboxName) => <Checkbox checked={ checkboxes[checkboxName] } onClick={ this.handleCheckboxClick }>{ checkboxName }</Checkbox>
                )}
            </div>
        )
    }

}

What I want magic updateUrlState function to do is get current URL query, update it (mark some checkbox as checked) and push results back to URL query.

Since this improvised URL state could be nested and complex, it probably should be serialised and stored as JSON, so resulting URL would look somewhat like that: https://example.com/#/page?checkboxes="{"foo":false,"bar":true}".

Is that possible?

roboslone
  • 2,847
  • 3
  • 20
  • 28

2 Answers2

0

Refer to this Modify the URL without reloading the page

You can simply dispatch an action when the checkbox is checked/unchecked that will invoke your magic url function to change the url using window.history.pushState()

function changeUrl() {
  isChecked = state.get('checkBox');
  window.history.pushState(state, '', 'isChecked=' + isChecked)
}

The first argument above is for when user clicks the back button, the browser will emit popstate event, and you can use the state to set your ui. See https://developer.mozilla.org/en-US/docs/Web/API/History_API

Then when the other user paste the url, you would want to just parse the url and set the correct state in the store in componentDidMount.

function decodeUrl() {
  let params = window.location.href.replace(baseUrl, '');
  // parse your params and set state
}

FYI: URL does not support certain characters (e.g. '{'). So you will want to parse them into query params, or encode them. Try this tool http://www.urlencoder.org/

Community
  • 1
  • 1
Edmund Lee
  • 2,514
  • 20
  • 29
  • There's no problem with modifying URL without reloading, I just don't want to do in manually every time user clicks on a checkbox. I'm looking for something like redux reducers, but for URL query. – roboslone Jan 29 '17 at 19:29
  • @roboslone not sure what you mean by something like redux reducers. you can dispatch the action and modify the url as state changes? – Edmund Lee Jan 29 '17 at 19:39
  • @roboslone https://github.com/reactjs/react-router-redux may help you. see `syncHistoryWithStore` – Edmund Lee Jan 29 '17 at 19:41
0

If I am getting your issue right, than redux middleware can really help you here. Middleware is a bit like a reduces, but sits in between the flow from the action to the reducer. Best example for such a use case is logging and I have developed middleware, which handles all remote requests. Basically you get the dispatched action in the middleware, there you can analyze it. In case of an action which should change the query parameter of the applycation, you can dispatch new actions from there.

So your code could look like this:

const queryChanges = store => next => action => {
  if (/*here you test the action*/) {
    //here can change the url, or
    return dispatch(actionToTriggerURLChange());

  } else {
    return next(action);
  }
}

In case you neither call return next(action), or dispatch a new one, just nothing will happen, so keep that in mind.

Have a look at the docs and figure out all the workings of middleware works and I guess this approach will be the most »non-repetitive« in this case.

philipp
  • 15,947
  • 15
  • 61
  • 106