2

Please note, that although the question itself is largely a duplicate of this, it concerns a different version which should support this. The linked question already accepted an answer on an old version

I'm pretty confused about what the intended workflow is.

Let's say I have a menu system where clicking on each item uses react-router to navigate to an area which pulls some data from the server.

url: yoursite/#/lists/countries
----------------------------
Billing Codes | <<Countries>> | Inventory Types 
---------------------------------------------------------
Countries:
---------------
Afghanistan
Azerbaijan
Belarus

with routes something like

Route #/lists component: Lists
   Route billing-codes component: BillingCodes
   Route countries component: Countries
   Route inventory-types component: InventoryTypes

I don't want to preload data from the server until an area is navigated to, so in my Countries component's on componentWillMount I fire an event (I'm using reflux but...whatever) that triggers a store to do an ajax request and update itself with the current list of countries.

Now the Countries component reacts to that change in state by updating the countries in its props. Except - reasonably - that generates an invariant error because I shouldn't be updating props on a child component, I should update it at the top level. But the top level is the router itself so now I'm just lost - where am I supposed to listen to changes and update props from?

(Cross-posted to the issue tracker as I think it needs some clearer documentation)

Community
  • 1
  • 1
George Mauer
  • 117,483
  • 131
  • 382
  • 612
  • possible duplicate of [React router - pass props on routes to child components](http://stackoverflow.com/questions/30871070/react-router-pass-props-on-routes-to-child-components) – Colin Ramsay Jul 03 '15 at 15:51
  • Appreciate it, but I *am* using 1.0 and still don't get it. Would be happy to see an answer on the original question or on here, though the original one seems to have an answer (now outdated) already accepted. – George Mauer Jul 03 '15 at 15:57
  • I asked similar question a while ago http://stackoverflow.com/questions/31168014/pass-object-through-link-in-react-router I gave some of the ideas to the problem, but don't have a clear answer yet – knowbody Jul 03 '15 at 16:07
  • You're using 1.0 of react-router? It's not even out yet and there's little documentation. There is a workaround in my suggested duplicate. – Colin Ramsay Jul 03 '15 at 16:12
  • its out on npm in beta2. There's docs inside their docs directory on github. I adjusted the topic to clarify that I'm specifically looking for the intended workflow in 1.0 – George Mauer Jul 03 '15 at 16:12
  • @ColinRamsay in addition, looking at your workaround answer - I'm not sure I understand how that helps when adjusting props after the fact like in my specific situation. Isn't that exactly what is generating the invariant error? – George Mauer Jul 03 '15 at 16:26

2 Answers2

3

Reading the react-router 0.13 -> 1.0 Upgrade Guide and this example led me to the following:

{ this.props.children && 
     React.cloneElement(this.props.children, {newprop: this.state.someobject }) }

Instead of including the child components directly, we clone them and inject the new properties. (The guard handles the case where there is no child component to be rendered.) Now, when the child component renders, it can access the needed property at this.props.newprop.

Adam Brown
  • 1,056
  • 10
  • 13
  • 1
    This approach is causing an error on the page load if either of the propTypes of the Route component are configured to be required. – Gajus Nov 11 '15 at 19:09
1

The easy way is to just use this.state, but if you absolutely have to use this.props then you should probably extend Router.createElement.

First add the createElement prop to your Router render.

React.render(
  <Router history={history} children={Routes} createElement={createElement} />,
  document.getElementById('app')
);

Wrap all of your components in a new Container component.

function createElement(Component, props) {
    return <Container component={Component} routerProps={props} />;
}

Your Container component will probably look something like this. Pass an onLoadData function down to your component.

import React from 'react';
import _ from 'lodash';

class Container extends React.Component {
  constructor(props) {
    super(props);
    this.state = { props: props.routerProps };
  }

  onLoadData(props) {
    var mergedProps = _.merge(this.state.props, props);
    this.setState({ props: mergedProps });
  }

  render() {
    var Component = this.props.component;
    return <Component {...this.state.props} onLoadData={this.onLoadData.bind(this)} />;
  }
}

Then from your loaded component, when you get your data back from the server, just fire this.props.onLoadData(data) and the data will be merged with your current props.

Read the Router.createElement Documentation

akira
  • 6,050
  • 29
  • 37
Brad
  • 1,880
  • 1
  • 12
  • 18
  • Actually, you might be able to just use something like lodash to merge your incoming data with your `this.props`, depending on the complexity. – Brad Jul 17 '15 at 05:25