如何将道具传递给{this.props.children}

我试图find正确的方式来定义一些可以通用的方式使用的组件:

<Parent> <Child value="1"> <Child value="2"> </Parent> 

当然在父子组件之间有一个逻辑是可以进行渲染的,你可以把<select><option>想象成这个逻辑的一个例子。

这是针对这个问题的虚拟实现:

 var Parent = React.createClass({ doSomething: function(value) { }, render: function() { return (<div>{this.props.children}</div>); } }); var Child = React.createClass({ onClick: function() { this.props.doSomething(this.props.value); // doSomething is undefined }, render: function() { return (<div onClick={this.onClick}></div>); } }); 

问题是,无论何时使用{this.props.children}定义一个包装器组件,如何将某些属性传递给它的所有子元素?

你可以使用React.Children遍历子元素,然后使用React.cloneElement方法用新的道具(浅层合并)克隆每个元素,例如:

 const Child = React.createClass({ render: function() { return <div onClick={() => this.props.doSomething(this.props.value)}>Click Me</div>; } }); const Parent = React.createClass({ doSomething: function(value) { console.log('doSomething called by child with value:', value); }, render: function() { const childrenWithProps = React.Children.map(this.props.children, (child) => React.cloneElement(child, { doSomething: this.doSomething }) ); return <div>{childrenWithProps}</div> } }); ReactDOM.render( <Parent> <Child value="1" /> <Child value="2" /> </Parent>, document.getElementById('container') ); 

小提琴: https : //jsfiddle.net/wqvmf7or/16/

对于一个稍微干净的方法来做到这一点,请尝试:

 <div> {React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })} </div> 

注意 :这只有在有一个孩子的情况下才有效,它是一个有效的React元素。

尝试这个

 <div>{React.cloneElement(this.props.children, {...this.props})}</div> 

它使用react-15.1为我工作。

情景1 – 通过道具来指导孩子。

查看所有其他答案

场景2 – 通过上下文通过组件树传递数据/依赖关系

继续阅读。

React的上下文使数据可用/启用dependency injection到所有的子和子组件,这里是一个例子

 class Child extends React.Component { render() { return ( <div onClick={() => this.context.doSomething(this.props.value)}> Click Me </div> ); } }; // Access parent context by defining contextTypes Child.contextTypes = { doSomething: React.PropTypes.func }; class Parent extends React.Component { // passes the information down to its children getChildContext() { return { doSomething : this.doSomething }; } doSomething(value) { console.log('doSomething called by child with value:', value); }, render() { return <div>{this.props.children}</div> } }; // make information available to its children Parent.childContextTypes = { doSomething: React.PropTypes.func }; ReactDOM.render( <Parent> <Child value="1" /> <Child value="2" /> </Parent>, document.getElementById('container') ); 

这篇文章。

免责声明:React的文档提到上下文是一个实验性的API,但是现在已经有多年没有改变了。 另外,在某些情况下确实需要它,这就是为什么最有名的React库(其中之一)在内部使用它的原因 – React Router 。 如果你想要超级安全的话,你可能需要以某种适合你的需求的方式来抽象上下文 。

你可以使用React.cloneElement ,最好在应用程序中开始使用它之前知道它是如何工作的。 它是在React v0.13介绍的,阅读更多的信息,所以一些这方面的工作给你:

 <div>{React.cloneElement(this.props.children, {...this.props})}</div> 

因此,请从React documentiota中为您理解它是如何工作的以及如何使用它们:

在React v0.13 RC2中,我们将引入一个类似于React.addons.cloneWithProps的新API,并带有这个签名:

 React.cloneElement(element, props, ...children); 

与cloneWithProps不同,这个新的函数没有任何神奇的内置行为来合并样式和className,这与我们没有从transferPropsTo获得该特性的原因是一样的。 没有人确定魔术事物的完整列表是什么,这使得难以推断代码,并且当样式具有不同的签名时(例如在即将到来的React Native中)难以重复使用。

React.cloneElement几乎等同于:

 <element.type {...element.props} {...props}>{children}</element.type> 

但是,与JSX和cloneWithProps不同,它也保留了引用。 这意味着如果你得到一个有关的孩子,你不会意外地从你的祖先偷走它。 你会得到相同的参考附加到你的新元素。

一个常见的模式是映射你的孩子,并添加一个新的道具。 有很多关于cloneWithProps丢失ref的问题,这使得更难推理你的代码。 现在,使用cloneElement的相同模式将按预期工作。 例如:

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

注意:React.cloneElement(child,{ref:'newRef'})是否覆盖了ref,因此除非您使用callback-refs,否则两个父母不可能有同一个孩子的ref。

这是进入React 0.13的关键特性,因为道具现在是不可变的。 升级path往往是克隆元素,但这样做可能会失去参考。 因此,我们需要一个更好的升级path。 当我们在Facebook上升级电话时,我们意识到我们需要这种方法。 我们从社区得到了同样的反馈。 因此,我们决定在最终发布之前再制作一个RC,以确保我们得到这个。

我们计划最终弃用React.addons.cloneWithProps。 我们现在还没有这样做,但是这是一个很好的机会开始考虑自己的使用,并考虑使用React.cloneElement。 在我们实际删除之前,我们一定会发布带有弃用通知的版本,因此不需要立即采取行动。

更多…

我需要修复上面的接受的答案,使其使用而不是这个指针。 在map函数的范围内没有定义doSomething函数。

 var Parent = React.createClass({ doSomething: function() { console.log('doSomething!'); }, render: function() { var that = this; var childrenWithProps = React.Children.map(this.props.children, function(child) { return React.cloneElement(child, { doSomething: that.doSomething }); }); return <div>{childrenWithProps}</div> }}) 

更新:这个修复是针对ECMAScript 5的,在ES6中没有必要在var那里= this

更清洁的方式考虑一个或多个孩子

 <div> { React.Children.map(this.props.children, child => React.cloneElement(child, {...this.props}))} </div> 

反应路由器版本0.x – 3.x是一团糟。 版本4是完整的重写正确的方法。

你不再需要{this.props.children} 。 现在你可以在Match使用componentrender来包装你的子组件,并像往常一样传递你的道具:

 <BrowserRouter> <div> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/posts">Posts</Link></li> <li><Link to="/about">About</Link></li> </ul> <hr/> <Route path="/" exact component={Home} /> <Route path="/posts" render={() => ( <Posts value1={1} value2={2} data={this.state.data} /> )} /> <Route path="/about" component={About} /> </div> </BrowserRouter> 

进一步@and_rest答案,这是我克隆的孩子,并添加一个类。

 <div className="parent"> {React.Children.map(this.props.children, child => React.cloneElement(child, {className:'child'}))} </div> 

没有一个答案解决了让孩子不是 React组件的问题,比如文本string。 解决方法可能是这样的:

 // Render method of Parent component render(){ let props = { setAlert : () => {alert("It works")} }; let childrenWithProps = React.Children.map( this.props.children, function(child) { if (React.isValidElement(child)){ return React.cloneElement(child, props); } return child; }); return <div>{childrenWithProps}</div> } 

这是你所要求的吗?

 var Parent = React.createClass({ doSomething: function(value) { } render: function() { return <div> <Child doSome={this.doSomething} /> </div> } }) var Child = React.createClass({ onClick:function() { this.props.doSome(value); // doSomething is undefined }, render: function() { return <div onClick={this.onClick}></div> } }) 

最光滑的方式来做到这一点:

  {React.cloneElement(this.props.children, this.props)} 

Parent.jsx:

 import React from 'react'; const doSomething = value => {}; const Parent = props => ( <div> { !props || !props.children ? <div>Loading... (required at least one child)</div> : !props.children.length ? <props.children.type {...props.children.props} doSomething={doSomething} {...props}>{props.children}</props.children.type> : props.children.map((child, key) => React.cloneElement(child, {...props, key, doSomething})) } </div> ); 

Child.jsx:

 import React from 'react'; /* but better import doSomething right here, or use some flux store (for example redux library) */ export default ({ doSomething, value }) => ( <div onClick={() => doSomething(value)}/> ); 

和main.jsx:

 import React from 'react'; import { render } from 'react-dom'; import Parent from './Parent'; import Child from './Child'; render( <Parent> <Child/> <Child value='1'/> <Child value='2'/> </Parent>, document.getElementById('...') ); 

看到这里的例子: https : //plnkr.co/edit/jJHQECrKRrtKlKYRpIWl?p=preview