react-router和 this.props.children - 如何将状态传递给 this.props.children

IT技术 reactjs react-router
2021-04-08 09:20:18

我是第一次使用 React-router,我还不知道如何思考。这是我在嵌套路由中加载组件的方式。

入口点 .js

ReactDOM.render(
    <Router history={hashHistory} >
        <Route path="/" component={App}>
            <Route path="models" component={Content}>
        </Route>
    </Router>, 
    document.getElementById('app')
);

应用程序.js

  render: function() {
    return (
      <div>
        <Header />
        {this.props.children}
      </div>
    );
  }

所以我的应用程序的孩子是我发送的内容组件。我正在使用 Flux 并且我的 App.js 有状态并监听更改,但我不知道如何将该状态传递给 this.props.children . 在使用 react-router 之前,我的 App.js 明确定义了所有子级,因此传递状态是很自然的,但我现在不知道该怎么做。

3个回答

这个问题归结为,你如何将props传递给孩子?

2018 年 6 月答案

今日技术:


假设一些有状态的组件:

import React from 'react'
import { BrowserRouter, Route } from 'react-router-dom'

// some component you made
import Title from './Title'

class App extends React.Component {
  // this.state
  state = { title: 'foo' }

  // this.render
  render() {
    return (
      <BrowserRouter>

        // when the url is `/test` run this Route's render function:
        <Route path="/:foobar" render={

          // argument is props passed from `<Route /`>
          routeProps => 

            // render Title component
            <Title 
              // pass this.state values
              title={this.state.title}

              // pass routeProps values (url stuff)
              page={routeProps.match.params.foobar} // "test"
            />

        } />

      </BrowserRouter>
    )
  }
}

这是有效的,因为this.props.children是一个函数:

// "smart" component aka "container"
class App extends React.Component {
  state = { foo: 'bar' }
  render() {
    return this.props.children(this.state.foo)
  }
}

// "dumb" component aka "presentational"
const Title = () => (
  <App>
    {title => <h1>{title}</h1>}
  </App>
)

代码沙盒上的示例

我以前不再推荐的旧学校答案:

使用几个 React 辅助方法,您可以添加状态、props和其他任何内容 this.props.children

render: function() {
  var children = React.Children.map(this.props.children, function (child) {
    return React.cloneElement(child, {
      foo: this.state.foo
    })
  })

  return <div>{children}</div>
}

然后你的子组件可以通过 props, 访问它this.props.foo

如果可以的话,通常建议不要改变数据。这通常是编程中的好习惯,而不仅仅是 React。只有在没有其他可能的解决方案时才进行突变——这是一种罕见的情况。
2021-05-27 09:20:18
我同意突变,但我想我没有看到联系 - ForEach 一定会改变状态吗?
2021-05-30 09:20:18
那么cloneElement返回一个新的子对象,所以你需要返回一个新的children数组/对象。Children.forEach不返回任何东西,也不返回任何东西Array.prototype.forEach如果您不返回任何内容,则不会影响任何更改,除非您更改某些内容或引起副作用(保存到数据库/进行 ajax 调用等)
2021-05-30 09:20:18
是的,我正在阅读有关儿童助手的内容并看到了这一点。为什么使用 Map 而不是 ForEach 呢?
2021-06-02 09:20:18
谢谢你的回答,就像一个魅力。但是,我收到来自子组件的 propType 验证警告,说道具未定义。我可以毫无问题地使用它们,但我不知道为什么会抛出这些警告?你有什么主意吗?
2021-06-03 09:20:18

您可以使用React 方法“cloneElement”来完成此操作。当你克隆元素时,你可以在那个时候传入 props。在渲染 fn 中使用克隆而不是原始。例如:

    render: function() {
    var childrenWithProps = React.cloneElement(this.props.children, {someProp: this.state.someProp});
    return (
      <div>
        <Header />
        {childrenWithProps}
      </div>
    );
  }
缺少花括号 childrenWithProps
2021-05-31 09:20:18
@Patrick 如果您查看我的回答,您会Children.map首先注意到这是必需的,通常是因为children可以是对象或数组,在后一种情况下,您克隆每个元素。
2021-06-05 09:20:18
@Patrick 正如@azium 所说,我的示例仅适用this.props.children于单个对象,而不是数组。并且,Children.map即使this.props.children是单个对象也能工作所以他们的建议比我的更好。
2021-06-08 09:20:18
在 this.props.children 只有一个元素的情况下,您的示例会克隆所有子项还是只克隆一个子项?我在 stackoverflow 上看到了另一个例子,但我需要手动遍历它们来完成基本的道具传递似乎是错误的。
2021-06-16 09:20:18
谢谢不管!我很高兴得到帮助
2021-06-16 09:20:18

还有使用Context的选项React-Router 依赖它来访问路由组件中的 Router 对象。

我在类似问题上给出的另一个答案中

我很快就在codepen上使用上下文组合了一个示例MainLayout使用 context:users定义子级可以使用的一些属性widgets这些属性由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'))