React-router: Calling a function in a component when a query parameter changes

Created on 8 Dec 2014  路  5Comments  路  Source: ReactTraining/react-router

Right now I'm working on moving some of our component state into query parameters so that pages are bookmarkable and other components can cleanly change the values.

It looks like react-router will call .render when a query parameter changes, but it's not clear how to tell which part of the URL / which query parameter changed, and the work I'm doing is outside of .render, in an onChange or componentDidUpdate lifecycle method.

Is there a clean way of implementing this? It seems like I either need to:

  • Duplicate the query parameters in the component's state so I know when they change
  • Or pass the query parameters down my view hierarchy from the router as a prop

Most helpful comment

@ryanflorence I believe you meant:

 componentWillReceiveProps (nextProps) {
    var old = this.props.query;
    var new = nextProps.query;
  }

All 5 comments

I was wondering the same. Any hints? :-)

Cheers,
Max

Edit: You should use https://github.com/rackt/react-router/issues/579#issuecomment-72350478, not this.

FWIW, here's my current "workaround":

/*
Usage:
this.setQuery("search","42");
this.onQueryChange("search", console.log.bind(console));
*/
var Navigation = _.extend({
    setQuery: function (dict) {
        var q = this.context.getCurrentQuery();
        for(var i in dict){
            if(dict.hasOwnProperty(i)){
                q[i] = dict[i] || undefined; //falsey values shall be removed.
            }
        }
        this.replaceWith(this.context.getCurrentPath(), this.context.getCurrentParams(), q);
    },
}, ReactRouter.Navigation);
_.extend(Navigation.contextTypes, ReactRouter.State.contextTypes);

var State = _.extend({
    getInitialState: function () {
        this._query = this.context.getCurrentQuery();
        this._queryWatches = [];
    },
    onQueryChange: function (key, callback) {
        this._queryWatches.push({
            key: key,
            callback: callback
        });
    },
    componentWillReceiveProps: function (nextProps, nextState) {
        var q = this.context.getCurrentQuery();
        for (var i = 0; i < this._queryWatches.length; i++) {
            var watch = this._queryWatches[i];
            if (this._query[watch.key] !== q[watch.key]) {
                watch.callback(this._query[watch.key], q[watch.key], watch.key);
            }
        }
        this._query = q;
    }
}, ReactRouter.State);

If I need that info I pass the params/query down my route view hierarchy and let react's lifecycle hooks help me out.

Router.run(routes, (Handler, state) => {
  React.render(<Handler {...state}/>, document.body);
});

// and then everywhere else:
<RouteHandler {...this.props}/>

Now its available here on every component:

  componentWillReceiveProps (nextProps) {
    var oldQuery = this.props.query;
    var newQuery = nextProps.query;
  }

_edit_ thanks @abergs

@ryanflorence I believe you meant:

 componentWillReceiveProps (nextProps) {
    var old = this.props.query;
    var new = nextProps.query;
  }

FWIW, in 0.13.2 this.context.router.getCurrentQuery() always returns the same object reference (which is then updated). This makes passing it down the hierarchy as a prop to compare it with nextProps impossible (this.props is already updated as well). Cloning before passing it down the hierarchy is a possible workaround.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

andrewpillar picture andrewpillar  路  3Comments

Waquo picture Waquo  路  3Comments

ryansobol picture ryansobol  路  3Comments

hgezim picture hgezim  路  3Comments

winkler1 picture winkler1  路  3Comments