Nicholas Murray Nicholas Murray - 11 days ago 4
React JSX Question

State value not updating when passed as route prop

I am having an issue with version 3 of the react-router where I pass state as a prop on the route as well as a click handler for updating the parent state value.

Although the parent sidebarOpen state value changes upon click in the Home component the sidebarState value remains at the initial value of false.

Is there a way to ensure that the updated state value is passed to the receiving component when the state changes in the router class?

first click:

sidebarOpen:false

sidebarState:false

second click:

sidebarOpen:true

sidebarState:false

third click:

sidebarOpen:false

sidebarState:false

var Home = React.createClass({
handleViewSidebar: function(){
this.props.route.clickHandler();
console.log('sidebarState:' + this.props.route.sidebarState);
},
render: function() {
return (
<div>
<Header onClick={ this.handleViewSidebar } />
<SideBar isOpen={ this.props.route.sidebarState } onClick={ this.handleViewSidebar } />
<Content isOpen={ this.props.route.sidebarState } />
</div>
);
}
});



var App = React.createClass({
getInitialState: function(){
return {sidebarOpen: false};
},
handleViewSidebar: function(){
this.setState({sidebarOpen: !this.state.sidebarOpen});
console.log('sidebarOpen:' + this.state.sidebarOpen);
},
render: function() {
return (
<Router history={ hashHistory }>
<Route path="/"
sidebarState={ this.state.sidebarOpen }
clickHandler={ this.handleViewSidebar }
component={ Home }>Home</Route>
<Route path="/about"
sidebarState={ this.state.sidebarOpen }
clickHandler={ this.handleViewSidebar }
component={ About }>About</Route>
<Route path="/contact"
sidebarState={ this.state.sidebarOpen }
clickHandler={ this.handleViewSidebar }
component={ Contact }>Contact</Route>
</Router>
);
}
});


ReactDOM.render(<App />, document.getElementById('app'));

Answer

My suggestion would be to restructure your component so that you have:

  • a root component that handle only the routing

  • an App component that will contain the components of the other routes as well as the handle sidebar logic and state

  • You other components (Home, About, Contact) will be child routes of App so can inherit the state and handlers via props.

You can then React.Children.map over this.props.chidren property (made available to App by React-Router when you nest routes within other routes) and then use React.cloneElement to create the children and assign them the state and handler via props.

So your code would look something like this:

var Routes = React.createClass({
  render: function() {
    return (
      <Router history={ hashHistory }>
        <Route path="/" component={App}>
          <IndexRoute component={ Home }>Home</Route>
          <Route path="/about" component={ About }>About</Route>
          <Route path="/contact" component={ Contact }>Contact</Route>
        </Route>
      </Router>
    );
  }
});

var App = React.createClass({
  getInitialState: function(){
    return {sidebarOpen: false};
  },
  handleViewSidebar: function(){  
    this.setState({sidebarOpen: !this.state.sidebarOpen});
  },
  render: function() {
    const childWithProps = 
      React.Children.map(
        this.props.children, 
          child => {
            return (
              React.cloneElement(child, {
                sidebarOpen : this.state.sidebarOpen,
                handleViewSidebar: this.handleViewSidebar
              }
            )
          }
      )
    return (
       <div>
         {childWithProps}
       </div>
    );
  }
});





ReactDOM.render(<App />, document.getElementById('app'));
Comments