编辑: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);
}
});
小提琴
带有子对象列表的父对象:您仍然拥有父对象上所需的所有内容,并且不需要使子对象更加复杂。
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);
}
});
小提琴
也可以使用this.handleChildClick.bind(null,childIndex),然后使用this.state.childrenData[childIndex]
注意,我们绑定的是一个空上下文,否则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} />
}
});
使用道具:
正如我上面所解释的,你已经在父组件中有了道具,所以传递整个子组件来访问道具是没有用的。
使用参考文献:
在事件中已经有了单击目标,在大多数情况下这就足够了。
另外,你可以直接在子对象上使用一个ref:
<Child ref="theChild" .../>
并访问父节点中的DOM节点
React.findDOMNode(this.refs.theChild)
对于更高级的情况,您希望访问父节点中子节点的多个引用,子节点可以直接在回调中传递所有dom节点。
组件有一个接口(props),父组件不应该假设子组件的内部工作,包括它的内部DOM结构或它声明引用的DOM节点。父组件使用子组件的ref意味着两个组件紧密耦合。
为了说明这个问题,我将引用Shadow DOM的这句话,它用于浏览器内部渲染滑块、滚动条、视频播放器等:
它们在您Web开发人员所能达到的范围之间创建了一个边界
以及所谓的实现细节,因此无法访问
你。然而,浏览器可以随意跨越这个边界。
有了这个边界,他们就能够构建所有HTML元素
使用相同的优秀的旧Web技术,脱离了div和span
就像你一样。
问题是,如果让子实现细节泄露到父进程中,就很难在不影响父进程的情况下重构子进程。这意味着作为库作者(或使用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>
)
小提琴
PERF WARNING (apply to ES5/ES6): if you are using PureComponent or shouldComponentUpdate, the above implementations will not be optimized by default because using onClick={e => doSomething()}, or binding directly during the render phase, because it will create a new function everytime the parent renders. If this is a perf bottleneck in your app, you can pass the data to the children, and reinject it inside "stable" callback (set on the parent class, and binded to this in class constructor) so that PureComponent optimization can kick in, or you can implement your own shouldComponentUpdate and ignore the callback in the props comparison check.
你也可以使用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)
在这种情况下,你可以优化子组件使用:
const OptimizedChild = onlyUpdateForKeys(['text'])(Child)