将道具传递给React.js中的父组件

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} /> } }); 

我知道你可以使用受控组件来传递一个input的值,但是通过整个工具包k'kaboodle会很好。 有时,子组件包含一组您不必查找的信息。

也许有办法将组件绑定到事件?

更新 – 2015年9月1日

在使用了React一年多的时间之后,在Sebastien Lorber的回答的激励下,我已经得出了将父母传递的子元素作为父母函数的参数,事实上并不是 React方式,也不是一个好主意。 我已经改变了答案。

编辑 :查看ES6更新示例的结束示例。

这个答案只是处理直接亲子关系的情况。 当父母和孩子有潜在的很多中介,检查这个答案 。

其他解决scheme都没有重点

虽然他们仍然正常工作,其他答案却错过了一些非常重要的东西

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} /> } }); 

使用道具 :正如我上面所解释的,你已经在父母的道具,所以通过整个子组件来访问道具是没有用的。

使用引用 :事件中已经有了点击目标,在大多数情况下,这已经足够了。 另外,你可以直接在孩子身上使用ref:

 <Child ref="theChild" .../> 

并用父级访问父节点中的DOM节点

 React.findDOMNode(this.refs.theChild) 

对于更高级的情况,如果你想访问父对象中多个子对象的引用,孩子可以直接在callback中传递所有的对象。

该组件有一个接口(道具),父母不应该假设任何关于孩子的内部工作,包括其内部的DOM结构或它声明的哪个DOM节点参考。 使用孩子的父母的父母意味着你紧紧地结合在一起的两个组成部分。

为了说明这个问题,我将引用这个关于Shadow DOM的引用,在浏览器中使用它来呈现滑块,滚动条,video播放器等内容:

他们在Web开发人员可以访问的内容和实现细节之间创build了一个界限,因此您无法访问。 然而,浏览器可以随意跨越这个边界。 有了这个边界,他们就可以使用相同的旧Web技术来构build所有的HTML元素,就像你想的那样。

问题是,如果你让子实现的细节泄漏到父项中,那么就很难重构子项而不影响父项。 这意味着作为一个图书馆作者(或作为一个Shadow DOM浏览器编辑器),这是非常危险的,因为你让客户端访问太多,这使得很难升级代码而不破坏逆向兼容性。

如果Chrome已经实现了滚动条,让客户端访问该滚动条的内部DOM节点,这意味着客户端可能会简单地打破该滚动条,并且当​​Chrome在重构之后执行自动更新时,该应用程序将更容易中断滚动条…而是只能访问一些安全的东西,比如用CSS定制滚动条的某些部分。

关于使用其他的东西

在callback中传递整个组件是很危险的,可能会导致新手开发人员执行非常奇怪的事情,比如调用childComponent.setState(...)childComponent.forceUpdate() ,或者在父代中分配新variables,从而使整个应用程序更难以推理。


编辑:ES6的例子

现在很多人使用ES6,下面是ES6语法的例子

孩子可以很简单:

 const Child = ({ onClick, text }) => ( <button onClick={onClick}> {text} </button> ) 

父类可以是一个类(它最终可以pipe理状态本身,但是我把它作为道具传递给它:

 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> ); } } 

但是如果不需要pipe理状态,它也可以被简化:

 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()} ,或者在呈现阶段直接绑定,每次父级渲染时都会创build一个新的函数。 如果这是应用程序中的性能瓶颈,则可以将数据传递给子级,然后在“稳定”callback(在父类中设置,并在类构造函数PureComponent绑定this级别)内重新注入,以便PureComponent优化可以启动,或者你可以实现你自己的shouldComponentUpdate并忽略道具比较检查中的callback。

您也可以使用重组库来提供更高阶的组件来实现微调优化:

 // 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) 

更新(2015年1月9日):OP已经使这个问题变成了一个动人的目标。 它已经被更新了。 所以,我觉得有责任更新我的答复。

首先,回答你提供的例子:

是的,这是可能的。

你可以通过更新Child的onClick来解决这个问题this.props.onClick.bind(null, this)

 var Child = React.createClass({ render: function () { return <a onClick={this.props.onClick.bind(null, this)}>Click me</a>; } }); 

您的Parent中的事件处理程序可以像这样访问组件和事件:

  onClick: function (component, event) { // console.log(component, event); }, 

JSBin快照

但这个问题本身就是误导

家长已经知道孩子的props

这在所提供的例子中是不清楚的,因为实际上没有提供道具。 此示例代码可能会更好地支持正在问的问题:

 var Child = React.createClass({ render: function () { return <a onClick={this.props.onClick}> {this.props.text} </a>; } }); var Parent = React.createClass({ getInitialState: function () { return { text: "Click here" }; }, onClick: function (event) { // event.component.props ?why is this not available? }, render: function() { return <Child onClick={this.onClick} text={this.state.text} />; } }); 

在这个例子中,你已经知道孩子的道具是什么了。

JSBin快照

如果真的是使用儿童的道具…

如果真的要使用儿童道具,可以避免与儿童完全结合。

JSX有一个传播属性 API,我经常在像Child这样的组件上使用。 它把所有的props并把它们应用到一个组件。 孩子看起来像这样:

 var Child = React.createClass({ render: function () { return <a {...this.props}> {this.props.text} </a>; } }); 

允许您直接在Parent中使用这些值:

 var Parent = React.createClass({ getInitialState: function () { return { text: "Click here" }; }, onClick: function (text) { alert(text); }, render: function() { return <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />; } }); 

JSBin快照

在连接其他子组件时,不需要额外的configuration

 var Parent = React.createClass({ getInitialState: function () { return { text: "Click here", text2: "No, Click here", }; }, onClick: function (text) { alert(text); }, render: function() { return <div> <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} /> <Child onClick={this.onClick.bind(null, this.state.text2)} text={this.state.text2} /> </div>; } }); 

JSBin快照

但我怀疑这不是你的实际使用情况。 所以让我们进一步挖掘…

一个强大的实际例子

所提供示例的一般性质很难说。 我已经创build了一个组件,演示了上述问题的实际用法,以非常实际的方式实现:

DTServiceCalculator工作示例
DTServiceCalculator回购

这个组件是一个简单的服务计算器。 您提供了一个服务列表(包含名称和价格),它将计算总计所选价格。

孩子们是无知的

在这个例子中, ServiceItem是子组件。 对外界没有太多的意见。 它需要一些道具 ,其中一个是点击时调用的函数。

<div onClick={this.props.handleClick.bind(this.props.index)} />

它只是用提供的index [ source ]调用提供的handleClickcallback函数。

父母是孩子

DTServicesCalculator是父组件就是这个例子。 这也是一个孩子。 我们看看吧。

DTServiceCalculator创build一个子组件( ServiceItem )的列表,并为它们提供道具[ source ]。 它是ServiceItem的父组件,但它是组件的子组件,它将它传递给列表。 它不拥有这些数据。 所以它再次将组件的处理委托给它的父组件源

<ServiceItem chosen={chosen} index={i} key={id} price={price} name={name} onSelect={this.props.handleServiceItem} />

handleServiceItem捕获索引,从孩子传递,并提供给它的父母[ 源 ]

 handleServiceClick (index) { this.props.onSelect(index); } 

业主知道一切

“所有权”的概念在React中是一个重要的概念。 我build议在这里阅读更多。

在我已经展示的例子中,我继续把一个事件的处理委托给组件树,直到我们到达拥有状态的组件。

当我们终于到达那里时,我们像处理[ source ]一样处理状态select/取消select:

 handleSelect (index) { let services = […this.state.services]; services[index].chosen = (services[index].chosen) ? false : true; this.setState({ services: services }); } 

结论

尽量保持你的最外面的组件尽可能不透明。 努力确保他们对于父母组件如何select实施它们的偏好很less。

请注意谁拥有您正在操作的数据。 在大多数情况下,您需要将处理树的事件委托给拥有该状态的组件。

另外: Flux模式是减less应用程序中这种必要的连接的好方法。

看起来有一个简单的答案。 考虑这个:

 var Child = React.createClass({ render: function() { <a onClick={this.props.onClick.bind(null, this)}>Click me</a> } }); var Parent = React.createClass({ onClick: function(component, event) { component.props // #=> {Object...} }, render: function() { <Child onClick={this.onClick} /> } }); 

关键是调用bind(null, this) this.props.onClick事件,从父this.props.onClick传递。 现在,onClick函数接受参数component AND event 。 我认为这是世界上最好的。

更新:9/1/2015

这是一个坏主意:让孩子实施细节泄漏给父母是不是一个好path。 见塞巴斯蒂安Lorber的答案。

基本上你使用道具来发送信息来自孩子和家长。

添加所有精彩的答案,让我给一个简单的例子,解释在React中从子元素到父元素的值传递

App.js

 class App extends React.Component { constructor(){ super(); this.handleFilterUpdate = this.handleFilterUpdate.bind(this); this.state={name:'igi'} } handleFilterUpdate(filterValue) { this.setState({ name: filterValue }); } render() { return ( <div> <Header change={this.handleFilterUpdate} name={this.state.name} /> <p>{this.state.name}</p> </div> ); } } 

Header.js

 class Header extends React.Component { constructor(){ super(); this.state={ names: 'jessy' } } Change(event) { // this.props.change(this.state.names); this.props.change('jessy'); } render() { return ( <button onClick={this.Change.bind(this)}>click</button> ); } } 

Main.js

 import React from 'react'; import ReactDOM from 'react-dom'; import App from './App.jsx'; ReactDOM.render(<App />, document.getElementById('app')); 

那就是,现在你可以将你的客户端的值传递给服务器。

看看Header.js中的Changefunction

 Change(event) { // this.props.change(this.state.names); this.props.change('jessy'); } 

这就是你如何将价值从客户端推送到服务器的道具

问题是如何将参数从子元素传递给父元素。 这个例子很容易使用和testing:

 //Child component class Child extends React.Component { render() { var handleToUpdate = this.props.handleToUpdate; return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div> ) } } //Parent component class Parent extends React.Component { constructor(props) { super(props); var handleToUpdate = this.handleToUpdate.bind(this); } handleToUpdate(someArg){ alert('We pass argument from Child to Parent: \n' + someArg); } render() { var handleToUpdate = this.handleToUpdate; return (<div> <Child handleToUpdate = {handleToUpdate.bind(this)} /></div>) } } if(document.querySelector("#demo")){ ReactDOM.render( <Parent />, document.querySelector("#demo") ); } 

看看JSFIDDLE

这是一个简单的3步ES6实现,在父构造函数中使用函数绑定。 这是官方反应教程build议的第一种方式(也有公共类字段语法不包括在这里)。 你可以在这里find所有这些信息https://reactjs.org/docs/handling-events.html

绑定父函数,让孩子们可以打电话给他们(并将数据传递给父母!:D)

  1. 确保在父构造函数中绑定你在父类中创build的函数
  2. 将绑定函数作为一个道具传递给孩子(没有lambda,因为我们正在传递一个参考函数)
  3. 调用一个子事件的绑定函数(Lambda!)当事件被触发时,我们调用函数,如果我们不这样做,函数将自动运行在加载状态,而不是在事件触发。

父函数

 handleFilterApply(filterVals){} 

父构造函数

 this.handleFilterApply = this.handleFilterApply.bind(this); 

道具传递给孩子

 onApplyClick = {this.handleFilterApply} 

儿童事件呼叫

 onClick = {() => {props.onApplyClick(filterVals)}