Reputation: 902
I am using react-router 2.0.0. Consider the following example:
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, IndexRoute, hashHistory } from 'react-router';
const Main = React.createClass({
getInitialState() {
return {data: 0};
},
componentDidMount() {
setInterval(() => {
console.log("Imagine I am polling data from a server here");
this.setState({data: Math.random().toFixed(2)});
}, 2000);
},
render() {
return <Router history={hashHistory}>
<Route path="/">
<IndexRoute component={MainPage} data={this.state.data}/>
<Route path="page1" component={Page1} data={this.state.data}/>
</Route>
</Router>;
}
});
const MainPage = React.createClass({
render() {
return <div>MainPage, data: {this.props.route.data}</div>;
}
});
const Page1 = React.createClass({
render() {
return <div>Page1, data: {this.props.route.data}</div>;
}
});
ReactDOM.render(<Main />, document.getElementById('app'));
My understanding of react.js is that data should usually be passed to child components as props. In the example I am polling some data from a server that should be used by two different child components. Since it is dynamic data I put it in the state.
However, if I define routing in that same component, react-router breaks down because it gets re-rendered. The updated state will not be passed to the child and the console will print:
Imagine I am polling data from a server here
Warning: [react-router] You cannot change <Router routes>; it will be ignored
One workaround that I dislike is to use global state to access the data. Which is what I did in my case.
Is there an elegant solution to this use-case?
Upvotes: 4
Views: 1613
Reputation: 688
Option 1: Facebook is offering a way to do this using contexts.
I quickly put together an example using contexts on codepen. MainLayout
defines some properties that could be used by the children using the context: users
and widgets
. These properties are used by the UserList
and WidgetList
components. Notice they need to define what they need to access from the context in the contextTypes
object.
var { Router, Route, IndexRoute, Link } = ReactRouter
var MainLayout = React.createClass({
childContextTypes: {
users: React.PropTypes.array,
widgets: React.PropTypes.array,
},
getChildContext: function() {
return {
users: ["Dan", "Ryan", "Michael"],
widgets: ["Widget 1", "Widget 2", "Widget 3"]
};
},
render: function() {
return (
<div className="app">
<header className="primary-header"></header>
<aside className="primary-aside">
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/users">Users</Link></li>
<li><Link to="/widgets">Widgets</Link></li>
</ul>
</aside>
<main>
{this.props.children}
</main>
</div>
)
}
})
var Home = React.createClass({
render: function() {
return (<h1>Home Page</h1>)
}
})
var SearchLayout = React.createClass({
render: function() {
return (
<div className="search">
<header className="search-header"></header>
<div className="results">
{this.props.children}
</div>
<div className="search-footer pagination"></div>
</div>
)
}
})
var UserList = React.createClass({
contextTypes: {
users: React.PropTypes.array
},
render: function() {
return (
<ul className="user-list">
{this.context.users.map(function(user, index) {
return <li key={index}>{user}</li>;
})}
</ul>
)
}
})
var WidgetList = React.createClass({
contextTypes: {
widgets: React.PropTypes.array
},
render: function() {
return (
<ul className="widget-list">
{this.context.widgets.map(function(widget, index) {
return <li key={index}>{widget}</li>;
})}
</ul>
)
}
})
var Routes = React.createClass({
render: function() {
return <Router>
<Route path="/" component={MainLayout}>
<IndexRoute component={Home} />
<Route component={SearchLayout}>
<Route path="users" component={UserList} />
<Route path="widgets" component={WidgetList} />
</Route>
</Route>
</Router>;
}
})
ReactDOM.render(<Routes/>, document.getElementById('root'))
Option 2: One solution I am using is the following: In the parent component render method, I do something like this:
{this.props.children && React.cloneElement(this.props.children, {
prop1: this.props.prop1,
prop2: this.props.prop2
})}
Then in all the components which are part of a child route, they will get these properties automatically, and can be accessed through their props
.
For example, in my project, Admin
is the parent component, check out its render method.
As you can see in the routes file, a component which is in a child route under /admin
, is PostList
. PostList
is using the function getAdminPost
which is coming from Admin
, using its props.
I suggest you define the routes in a separate file, like described in the react-router tutorial.
Upvotes: 4