如何将状态从路由器传递到子组件

时间:2016-03-05 21:09:57

标签: reactjs react-router

我正在使用react-router 2.0.0。请考虑以下示例:

 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'));

我对react.js的理解是,数据通常应该作为道具传递给子组件。 在示例中,我正在轮询来自服务器的一些数据,这些数据应由两个不同的子组件使用。因为它是动态数据我把它放在状态。

但是,如果我在同一个组件中定义路由,则react-router会因为重新呈现而崩溃。更新后的状态不会传递给孩子,控制台将打印:

Imagine I am polling data from a server here
Warning: [react-router] You cannot change <Router routes>; it will be ignored

我不喜欢的一种解决方法是使用全局状态来访问数据。这就是我在我的案例中所做的。

这个用例有优雅的解决方案吗?

1 个答案:

答案 0 :(得分:4)

选项1: Facebook正在使用contexts提供一种方法。

我在codepen上使用上下文快速汇总了一个示例。 MainLayout定义了一些可以由孩子使用上下文的属性:userswidgetsUserListWidgetList组件使用这些属性。请注意,他们需要从contextTypes对象中的上下文定义他们需要访问的内容。

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'))

选项2: 我使用的一个解决方案如下: 在父组件渲染方法中,我做了类似这样的事情:

{this.props.children && React.cloneElement(this.props.children, {
   prop1: this.props.prop1,
   prop2: this.props.prop2
})}

然后,在作为子路线一部分的所有组件中,他们将自动获得这些属性,并可通过他们的props访问。

例如,在my project中,Admin是父组件,请查看其render method

正如您在routes file中看到的,位于/admin下的子路径中的组件是PostListPostList使用的函数getAdminPost来自Adminusing its props

我建议您在单独的文件中定义路线,如react-router tutorial中所述。

相关问题