编辑:请参见ES6更新示例的最终示例。
该答案仅处理直接的父子关系。当父母和孩子可能有很多中介人时,请检查此答案。
其他解决方案遗漏了重点
尽管它们仍然可以正常工作,但其他答案缺少一些非常重要的内容。
在React.js中,没有一种简单的方法可以通过事件将孩子的道具传递给其父对象吗?
父母已经有了那个孩子的道具!:如果孩子有道具,那是因为它的父母为孩子提供了道具!为什么您要孩子在父母显然已经有了那个道具的情况下将道具传回给父母呢?
更好的实施
子:实际上不必比这更复杂。
var Child = React.createClass({
render: function () {
return <button onClick={this.props.onClick}>{this.props.text}</button>;
},
});
有独生子女的父母:使用传递给子女的值
var Parent = React.createClass({
getInitialState: function() {
return {childText: "Click me! (parent prop)"};
},
render: function () {
return (
<Child onClick={this.handleChildClick} text={this.state.childText}/>
);
},
handleChildClick: function(event) {
// You can access the prop you pass to the children
// because you already have it!
// Here you have it in state but it could also be
// in props, coming from another parent.
alert("The Child button text is: " + this.state.childText);
// You can also access the target of the click here
// if you want to do some magic stuff
alert("The Child HTML is: " + event.target.outerHTML);
}
});
JsFiddle
有孩子清单的父母:您仍然拥有父母所需要的一切,不需要让孩子变得更复杂。
var Parent = React.createClass({
getInitialState: function() {
return {childrenData: [
{childText: "Click me 1!", childNumber: 1},
{childText: "Click me 2!", childNumber: 2}
]};
},
render: function () {
var children = this.state.childrenData.map(function(childData,childIndex) {
return <Child onClick={this.handleChildClick.bind(null,childData)} text={childData.childText}/>;
}.bind(this));
return <div>{children}</div>;
},
handleChildClick: function(childData,event) {
alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
alert("The Child HTML is: " + event.target.outerHTML);
}
});
JsFiddle
也可以先使用this.handleChildClick.bind(null,childIndex)
再使用this.state.childrenData[childIndex]
注意我们绑定了null
上下文,因为否则React会发出与其自动绑定系统相关的警告。使用null意味着您不想更改函数上下文。另请参阅。
关于封装和耦合的其他答案
对于耦合和封装而言,这对我来说是个坏主意:
var Parent = React.createClass({
handleClick: function(childComponent) {
// using childComponent.props
// using childComponent.refs.button
// or anything else using childComponent
},
render: function() {
<Child onClick={this.handleClick} />
}
});
使用props:如上所述,您已经在父级中拥有了props,因此将整个子组件传递给props是没有用的。
使用refs:您已经有了事件的点击目标,在大多数情况下,这已经足够。另外,您可以直接在子对象上使用ref:
<Child ref="theChild" .../>
并使用以下命令访问父节点中的DOM节点
React.findDOMNode(this.refs.theChild)
对于更高级的情况,如果您想访问父级中子级的多个引用,则子级可以直接在回调中传递所有dom节点。
该组件具有一个接口(props),并且父组件不应承担任何有关子组件内部工作的事情,包括其内部DOM结构或为其声明引用的DOM节点。父母使用孩子的ref意味着您将这两个组件紧密地结合在一起。
为了说明这个问题,我将引用有关Shadow DOM的报价,该报价在浏览器内部用于渲染滑块,滚动条,视频播放器等内容:
他们在Web开发人员可以访问的范围和所认为的实现细节之间建立了界限,因此您无法访问。但是,浏览器可以随意跨越此边界。有了这个边界,他们就可以使用相同的古老Web技术来构建所有HTML元素,就像在您的div和跨度中一样。
问题是,如果让子实现细节泄漏到父中,那么很难重构子而不影响父。这意味着作为库作者(或使用Shadow DOM的浏览器编辑器),这是非常危险的,因为您让客户端访问过多,这使得在不破坏可追溯性的情况下很难升级代码。
如果Chrome浏览器实现了其滚动条,让客户端可以访问该滚动条的内部dom节点,则这意味着客户端可以简单地破坏该滚动条,并且当Chrome浏览器在重构后重新执行其自动更新时,应用程序更容易崩溃滚动条...相反,它们只允许访问一些安全的东西,例如使用CSS自定义滚动条的某些部分。
关于使用其他任何东西
在回调中传递整个组件很危险,并且可能会使新手开发人员做非常奇怪的事情,例如在父级内部调用childComponent.setState(...)
或childComponent.forceUpdate()
,或者为其分配新变量,这使得整个应用程序更难以推理。
编辑:ES6示例
由于许多人现在使用ES6,因此以下是ES6语法的示例
孩子可以很简单:
const Child = ({
onClick,
text
}) => (
<button onClick={onClick}>
{text}
</button>
)
父级可以是一个类(它最终可以管理状态本身,但是我在这里将其作为道具传递:
class Parent1 extends React.Component {
handleChildClick(childData,event) {
alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
alert("The Child HTML is: " + event.target.outerHTML);
}
render() {
return (
<div>
{this.props.childrenData.map(child => (
<Child
key={child.childNumber}
text={child.childText}
onClick={e => this.handleChildClick(child,e)}
/>
))}
</div>
);
}
}
但是,如果不需要管理状态,也可以将其简化:
const Parent2 = ({childrenData}) => (
<div>
{childrenData.map(child => (
<Child
key={child.childNumber}
text={child.childText}
onClick={e => {
alert("The Child button data is: " + child.childText + " - " + child.childNumber);
alert("The Child HTML is: " + e.target.outerHTML);
}}
/>
))}
</div>
)
JsFiddle
PERF警告(适用于ES5 / ES6):如果使用PureComponent
或shouldComponentUpdate
,则默认情况下不会优化上述实现onClick={e => doSomething()}
,因为在渲染阶段直接使用或绑定,因为每次父渲染时都会创建一个新函数。如果这是应用程序中的性能瓶颈,则可以将数据传递给子级,然后将其重新注入“稳定”回调(在父类中设置,并绑定到this
类构造函数中),以便可以进行PureComponent
优化,或者可以实现自己的,shouldComponentUpdate
并在道具比较检查中忽略回调。
您还可以使用Recompose库,它提供了更高阶的组件来实现微调的优化:
// A component that is expensive to render
const ExpensiveComponent = ({ propA, propB }) => {...}
// Optimized version of same component, using shallow comparison of props
// Same effect as React's PureRenderMixin
const OptimizedComponent = pure(ExpensiveComponent)
// Even more optimized: only updates if specific prop keys have changed
const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)
在这种情况下,您可以使用以下方法优化Child组件:
const OptimizedChild = onlyUpdateForKeys(['text'])(Child)