将道具传递给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 ]调用提供的handleClick
callback函数。
父母是孩子
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)
- 确保在父构造函数中绑定你在父类中创build的函数
- 将绑定函数作为一个道具传递给孩子(没有lambda,因为我们正在传递一个参考函数)
- 调用一个子事件的绑定函数(Lambda!)当事件被触发时,我们调用函数,如果我们不这样做,函数将自动运行在加载状态,而不是在事件触发。
父函数
handleFilterApply(filterVals){}
父构造函数
this.handleFilterApply = this.handleFilterApply.bind(this);
道具传递给孩子
onApplyClick = {this.handleFilterApply}
儿童事件呼叫
onClick = {() => {props.onApplyClick(filterVals)}