编辑:查看 ES6 更新示例的结尾示例。
这个答案只是处理直接父子关系的情况。当父母和孩子可能有很多中介时,请查看answer。
其他解决方案没有抓住重点
虽然它们仍然可以正常工作,但其他答案缺少一些非常重要的东西。
在 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 会发出与其 autobinding 系统相关的警告。使用 null 意味着您不想更改函数上下文。 See also.
关于其他答案中的封装和耦合
就耦合和封装而言,这对我来说是一个坏的想法:
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 是没有用的。
使用引用:
您已经在事件中拥有了点击目标,在大多数情况下这已经足够了。
此外,您可以直接在孩子身上使用 ref:
<Child ref="theChild" .../>
并使用
访问父节点中的DOM节点
React.findDOMNode(this.refs.theChild)
对于更高级的情况,您希望在父级中访问子级的多个 ref,子级可以直接在回调中传递所有 dom 节点。
组件有一个接口(props),父组件不应该假设子组件的内部工作,包括它的内部 DOM 结构或它为哪些 DOM 节点声明 refs。使用孩子的 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>
)
JsFiddle
PERF WARNING(适用于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)
在这种情况下,您可以使用以下方法优化子组件:
const OptimizedChild = onlyUpdateForKeys(['text'])(Child)