在 React.js 中将 props 传递给父组件 [英] Pass props to parent component in React.js

查看:42
本文介绍了在 React.js 中将 props 传递给父组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 React.js 中,是否没有一种简单的方法可以使用事件将子项的 props 传递给其父项?

var Child = React.createClass({渲染:函数(){<a onClick={this.props.onClick}>点击我</a>}});var Parent = React.createClass({onClick:函数(事件){//event.component.props ?为什么不可用?},渲染:函数(){<Child onClick={this.onClick}/>}});

我知道您可以使用受控组件来传递输入值,但最好传递整个 kit n'kaboodle.有时子组件包含一组您不想查找的信息.

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

更新 – 2015 年 9 月 1 日

在使用 React 一年多之后,在 Sebastien Lorber 的回答的推动下,我得出结论,将子组件作为参数传递给父组件中的函数实际上不是 React 方式,也不是永远是个好主意.我换了答案.

解决方案

Edit:ES6 更新示例见最终示例.

这个答案只是处理直接父子关系的情况.如果父母和孩子可能有很多中介,请查看此答案.

其他解决方案没有抓住重点

虽然它们仍然可以正常工作,但其他答案遗漏了一些非常重要的东西.

<块引用>

在 React.js 中,是否没有一种简单的方法可以使用事件将子项的 props 传递给其父项?

父级已经有了那个子道具!:如果子级有道具,那是因为它的父级为子级提供了那个道具!为什么你想让孩子把道具传回给父母,而父母显然已经有了那个道具?

更好的实施

孩子:真的没有必要比这更复杂.

var Child = React.createClass({渲染:函数(){return <button onClick={this.props.onClick}>{this.props.text}</button>;},});

Parent with single child:使用它传递给孩子的值

var Parent = React.createClass({getInitialState:函数(){return {childText: "点击我!(父道具)"};},渲染:函数(){返回 (<Child onClick={this.handleChildClick} text={this.state.childText}/>);},handleChildClick:函数(事件){//你可以访问你传递给孩子的道具//因为你已经拥有了!//这里你有它的状态,但它也可能是//在道具中,来自另一个父级.alert("子按钮的文字是:" + this.state.childText);//也可以在这里访问点击的目标//如果你想做一些神奇的事情alert("子 HTML 是:" + event.target.outerHTML);}});

JsFiddle

带有子项列表的父项:您仍然拥有父项所需的一切,不需要让子项变得更复杂.

var Parent = React.createClass({getInitialState:函数(){返回 {childrenData: [{childText: "点击我 1!", childNumber: 1},{childText:点击我 2!",childNumber:2}]};},渲染:函数(){var children = this.state.childrenData.map(function(childData,childIndex) {return <Child onClick={this.handleChildClick.bind(null,childData)} text={childData.childText}/>;}.bind(this));返回 <div>{children}</div>;},handleChildClick:函数(childData,事件){alert("子按钮数据为:" + childData.childText + " - " + childData.childNumber);alert("子 HTML 是:" + event.target.outerHTML);}});

JsFiddle

也可以使用this.handleChildClick.bind(null,childIndex),然后使用this.state.childrenData[childIndex]

注意我们绑定了一个 null 上下文,否则 React 会发出与其 自动绑定 系统.使用 null 意味着您不想更改函数上下文.另见.

关于其他答案中的封装和耦合

就耦合和封装而言,这对我来说是一个糟糕的想法:

var Parent = React.createClass({句柄点击:函数(子组件){//使用 childComponent.props//使用 childComponent.refs.button//或其他任何使用 childComponent 的东西},渲染:函数(){<Child onClick={this.handleClick}/>}});

使用道具:正如我上面解释的,你已经在父组件中拥有了 props,所以传递整个子组件来访问 props 是没有用的.

使用引用:您已经在事件中拥有了点击目标,并且在大多数情况下这已经足够了.此外,您可以直接在孩子上使用 ref:

并使用

访问父节点中的DOM节点

React.findDOMNode(this.refs.theChild)

对于更高级的情况,您要访问父级中子级的多个引用,子级可以直接在回调中传递所有 dom 节点.

组件有一个接口(props),父组件不应该对子组件的内部工作做任何假设,包括它的内部 DOM 结构或它声明了 refs 的 DOM 节点.父级使用子级的 ref 意味着您将两个组件紧密耦合.

为了说明这个问题,我将引用关于 Shadow DOM,用于在浏览器中渲染滑块、滚动条、视频播放器等内容...:

<块引用>

他们在您、Web 开发人员可以达到的范围之间建立了界限以及什么被认为是实现细节,因此无法访问你.然而,浏览器可以随意跨越这个边界.有了这个边界,他们就能够构建所有的 HTML 元素使用相同的旧 Web 技术,脱离 divs 和 spans就像你一样.

问题在于,如果让子实现细节泄漏到父中,则很难在不影响父的情况下重构子.这意味着作为库作者(或作为使用 Shadow DOM 的浏览器编辑器)这是非常危险的,因为您让客户端访问太多,这使得在不破坏复古兼容性的情况下升级代码变得非常困难.

如果 Chrome 已实现其滚动条,让客户端访问该滚动条的内部 dom 节点,这意味着客户端可能有可能简单地破坏该滚动条,并且当​​ Chrome 执行其自动更新时,应用程序将更容易中断在重构滚动条之后……相反,它们只允许访问一些安全的东西,例如使用 CSS 自定义滚动条的某些部分.

关于使用其他任何东西

在回调中传递整个组件是危险的,可能会导致新手开发者做一些非常奇怪的事情,比如调用 childComponent.setState(...)childComponent.forceUpdate()code>,或者在父级内部为其分配新变量,从而使整个应用程序更难以推理.

<小时>

ES6 示例

由于现在很多人都在使用 ES6,这里是 ES6 语法的相同示例

孩子可以很简单:

const Child = ({点击,文本}) =>(<button onClick={onClick}>{文本})

父级可以是一个类(它最终可以自己管理状态,但我在这里将它作为道具传递:

class Parent1 扩展 React.Component {handleChildClick(childData,事件){alert("子按钮数据为:" + childData.childText + " - " + childData.childNumber);alert("子 HTML 是:" + event.target.outerHTML);}使成为() {返回 (<div>{this.props.childrenData.map(child => (<孩子键={child.childNumber}文本={child.childText}onClick={e =>this.handleChildClick(child,e)}/>))}

);}}

但是如果不需要管理状态也可以简化:

const Parent2 = ({childrenData}) =>(<div>{childrenData.map(child => (<孩子键={child.childNumber}文本={child.childText}onClick={e =>{alert("子按钮数据为:" + child.childText + " - " + child.childNumber);alert("子 HTML 是:" + e.target.outerHTML);}}/>))}

)

JsFiddle

<小时>

PERF WARNING(适用于 ES5/ES6):如果您使用 PureComponentshouldComponentUpdate,上述实现将不会被优化默认是因为使用 onClick={e =>doSomething()},或者在渲染阶段直接绑定,因为它会在每次父渲染时创建一个新函数.如果这是您的应用程序中的性能瓶颈,您可以将数据传递给子项,并将其重新注入稳定"回调中(在父类上设置,并在类构造函数中绑定到 this),以便PureComponent 优化可以启动,或者您可以实现自己的 shouldComponentUpdate 并忽略道具比较检查中的回调.

您还可以使用 Recompose 库,该库提供更高阶的组件以实现微调优化:

//渲染成本高的组件const ExpensiveComponent = ({ propA, propB }) =>{...}//相同组件的优化版本,使用props的浅比较//与 React 的 PureRenderMixin 效果相同const OptimizedComponent = pure(ExpensiveComponent)//更优化:仅当特定的 prop 键改变时才更新const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)

在这种情况下,您可以使用以下方法优化 Child 组件:

const OptimizedChild = onlyUpdateForKeys(['text'])(Child)

Is there not a simple way to pass a child's props to its parent using events, in React.js?

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

I know you can use controlled components to pass an input's value but it'd be nice to pass the whole kit n' kaboodle. Sometimes the child component contains a set of information you'd rather not have to look up.

Perhaps there's a way to bind the component to the event?

UPDATE – 9/1/2015

After using React for over a year, and spurred on by Sebastien Lorber's answer, I've concluded passing child components as arguments to functions in parents is not in fact the React way, nor was it ever a good idea. I've switched the answer.

解决方案

Edit: see the end examples for ES6 updated examples.

This answer simply handle the case of direct parent-child relationship. When parent and child have potentially a lot of intermediaries, check this answer.

Other solutions are missing the point

While they still work fine, other answers are missing something very important.

Is there not a simple way to pass a child's props to its parent using events, in React.js?

The parent already has that child prop!: if the child has a prop, then it is because its parent provided that prop to the child! Why do you want the child to pass back the prop to the parent, while the parent obviously already has that prop?

Better implementation

Child: it really does not have to be more complicated than that.

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

Parent with single child: using the value it passes to the child

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

Parent with list of children: you still have everything you need on the parent and don't need to make the child more complicated.

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

It is also possible to use this.handleChildClick.bind(null,childIndex) and then use this.state.childrenData[childIndex]

Note we are binding with a null context because otherwise React issues a warning related to its autobinding system. Using null means you don't want to change the function context. See also.

About encapsulation and coupling in other answers

This is for me a bad idea in term of coupling and encapsulation:

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

Using props: As I explained above, you already have the props in the parent so it's useless to pass the whole child component to access props.

Using refs: You already have the click target in the event, and in most case this is enough. Additionnally, you could have used a ref directly on the child:

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

And access the DOM node in the parent with

React.findDOMNode(this.refs.theChild)

For more advanced cases where you want to access multiple refs of the child in the parent, the child could pass all the dom nodes directly in the callback.

The component has an interface (props) and the parent should not assume anything about the inner working of the child, including its inner DOM structure or which DOM nodes it declares refs for. A parent using a ref of a child means that you tightly couple the 2 components.

To illustrate the issue, I'll take this quote about the Shadow DOM, that is used inside browsers to render things like sliders, scrollbars, video players...:

They created a boundary between what you, the Web developer can reach and what’s considered implementation details, thus inaccessible to you. The browser however, can traipse across this boundary at will. With this boundary in place, they were able to build all HTML elements using the same good-old Web technologies, out of the divs and spans just like you would.

The problem is that if you let the child implementation details leak into the parent, you make it very hard to refactor the child without affecting the parent. This means as a library author (or as a browser editor with Shadow DOM) this is very dangerous because you let the client access too much, making it very hard to upgrade code without breaking retrocompatibility.

If Chrome had implemented its scrollbar letting the client access the inner dom nodes of that scrollbar, this means that the client may have the possibility to simply break that scrollbar, and that apps would break more easily when Chrome perform its auto-update after refactoring the scrollbar... Instead, they only give access to some safe things like customizing some parts of the scrollbar with CSS.

About using anything else

Passing the whole component in the callback is dangerous and may lead novice developers to do very weird things like calling childComponent.setState(...) or childComponent.forceUpdate(), or assigning it new variables, inside the parent, making the whole app much harder to reason about.


Edit: ES6 examples

As many people now use ES6, here are the same examples for ES6 syntax

The child can be very simple:

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

The parent can be either a class (and it can eventually manage the state itself, but I'm passing it as props here:

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

But it can also be simplified if it does not need to manage state:

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 (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.

You can also use Recompose library, which provide higher order components to achieve fine-tuned optimisations:

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

In this case you could optimize the Child component by using:

const OptimizedChild = onlyUpdateForKeys(['text'])(Child)

这篇关于在 React.js 中将 props 传递给父组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆