props在React.js中,没有一种简单的方法可以通过事件将子对象传递给其父对象吗?
props
var Child = React.createClass({ render: function() { <a onClick={this.props.onClick}>Click me</a> } }); var Parent = React.createClass({ onClick: function(event) { // event.component.props ?why is this not available? }, render: function() { <Child onClick={this.onClick} /> } });
我知道您可以使用受控组件来传递输入值,但是最好传递整个工具包n’kaboodle。有时,子组件包含一些您不想查找的信息。
也许有一种将组件绑定到事件的方法?
使用后反应了一年多,和塞巴斯蒂安·洛伯的回答的刺激下,我已经得出结论传递子组件作为参数传递给函数的父母是 不是 事实上的方式做出反应,也不是永远的好主意。我已经换了答案。
编辑 :请参见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); } });
也可以先使用this.handleChildClick.bind(null,childIndex)再使用this.state.childrenData[childIndex]
this.handleChildClick.bind(null,childIndex)
this.state.childrenData[childIndex]
注意我们绑定了null上下文,因为否则React会发出与其自动绑定系统有关的警告。使用null意味着您不想更改函数上下文。另请参阅。
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} /> } });
使用道具 :如前所述,您已经在父级中拥有道具,因此将整个子组件传递给道具是没有用的。
使用refs :您已经有了事件的点击目标,在大多数情况下,这已经足够。另外,您可以直接在子对象上使用ref:
<Child ref="theChild" .../>
并使用以下命令访问父节点中的DOM节点
React.findDOMNode(this.refs.theChild)
对于更高级的情况,您想访问父级中子级的多个引用,则子级可以直接在回调中传递所有dom节点。
该组件具有一个接口(props),并且父组件不应承担有关子组件内部工作的任何事情,包括其内部DOM结构或为其声明引用的DOM节点。父母使用孩子的ref意味着您将这两个组件紧密耦合。
为了说明这个问题,我将引用有关Shadow DOM的引用,该引用在浏览器内部用于渲染滑块,滚动条,视频播放器等内容:
他们在Web开发人员可以访问的内容与所认为的实现细节之间建立了界限,因此您无法访问。但是,浏览器可以随意跨越此边界。有了这个边界,他们就可以使用相同的古老Web技术来构建所有HTML元素,就像您将要在div和span中那样。
问题是,如果让子实现细节泄漏到父中,则很难重构子而不影响父。这意味着作为库作者(或使用Shadow DOM的浏览器编辑器),这是非常危险的,因为您让客户端访问过多,这使得在不破坏兼容性的情况下很难升级代码。
如果Chrome浏览器已实现其滚动条,允许客户端访问该滚动条的内部dom节点,则这意味着客户端可以简单地破坏该滚动条,并且当Chrome浏览器重构后执行自动更新时,应用程序将更容易崩溃。滚动条…相反,它们只允许访问一些安全的东西,例如使用CSS自定义滚动条的某些部分。
关于使用其他任何东西
在回调中传递整个组件很危险,并且可能会使新手开发人员做非常奇怪的事情,例如在父级内部调用childComponent.setState(...)或childComponent.forceUpdate(),或者给它分配新变量,这使得整个应用程序更难以推理。
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警告 (适用于ES5 / ES6):如果使用PureComponent或shouldComponentUpdate,则默认情况下不会优化上述实现onClick={e => doSomething()},因为在渲染阶段直接使用或绑定,因为每次父渲染时都会创建一个新函数。如果这是应用程序中的性能瓶颈,则可以将数据传递给子级,然后将其重新注入“稳定”回调(在父类中设置,并绑定到this类构造函数中),以便可以进行PureComponent优化,或者可以自己执行,shouldComponentUpdate而忽略道具比较检查中的回调。
PureComponent
shouldComponentUpdate
onClick={e => doSomething()}
this
您还可以使用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)