2016-03-05 73 views
2

我正在使用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 

一個解決辦法,我不喜歡的是使用全局狀態來訪問數據。這是我在我的情況下做的。

這個用例是否有一個優雅的解決方案?

回答

4

選項1: Facebook提供了一種使用contexts的方法。

我很快就在codepen上使用上下文的例子。 MainLayout定義了兒童使用上下文可以使用的一些屬性:userswidgets。這些屬性由UserListWidgetList組件使用。請注意,他們需要從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正在使用來自Adminusing its props的功能getAdminPost

我建議你在一個單獨的文件中定義路線,如react-router tutorial中所述。

+0

讓我將該解決方案轉移到我的示例中: 我將引入一個新組件,如下所示:''。按照您所描述的方式將數據存儲在其狀態中,並使其通過'cloneElement'東西傳播數據更新。 嗯,有趣的,至少這可以避免全球狀態,但它感覺很hacky。 這就像手動執行react.js應該處理的狀態更新內容。 另外,道具傳遞真的隱藏在'DataComponent'裏面。 將它明確表示爲HTML中的屬性更容易掌握。 –

+0

我同意它不是那麼棒的解決方案。現在我想到了,我記得我正在研究[contexts](https://facebook.github.io/react/docs/context.html),只是我不記得爲什麼我沒有'用它們並決定去找那個解決方案。上下文是這樣做的官方解決方案,但他們不建議濫用它。理想情況下,如果你知道你的孩子組件,你可以簡單地將屬性作爲屬性傳遞給它們。 –

+0

作爲屬性傳遞並不真的工作,這就是我的例子。那麼當你有狀態時它不起作用,因爲當它被重新渲染時,它會中斷。 –

相關問題