首页 文章

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

提问于
浏览
235

在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会很好 . 有时,子组件包含一组您不必查找的信息 .

也许有一种方法将组件绑定到事件?

更新 - 2015年9月1日

在使用React超过一年之后,在Sebastien Lorber的推动下,通过传递子组件作为父母功能的参数实际上并不是React方式,也不是一个好主意 . 我已经改变了答案 .

6 回答

  • 224

    问题是如何将参数从子组件传递到父组件 . 这个例子易于使用和测试:

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

    Look at JSFIDDLE

  • 24

    看来有一个简单的答案 . 考虑一下:

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

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

    更新:2015年9月1日

    这是一个坏主意:让子实现细节泄露给父母从来都不是一条好路 . 见Sebastien Lorber的回答 .

  • 1

    Basically you use props to send information to and from Child and Parent.

    添加所有精彩答案,让我举一个简单的例子来解释在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中的Change函数

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

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

  • 6

    这是在父构造函数中使用函数绑定的简单3步ES6实现 . 这是官方反应教程推荐的第一种方式(此处还没有涉及公共类字段语法) . 你可以在这里找到所有这些信息https://reactjs.org/docs/handling-events.html

    绑定父函数,以便孩子可以调用它们(并将数据传递给父母!:D)

    • 确保在父构造函数中绑定在父级中创建的函数

    • 将绑定函数作为prop传递给子节点(No lambda,因为我们将ref传递给函数)

    • 从子事件中调用绑定函数(Lambda!我们're calling the function when the event is fired. If we don' t这样做,该函数将在加载时自动运行,而不会在事件上触发 . )

    父功能

    handleFilterApply(filterVals){}
    

    父构造函数

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

    通过儿童的道具

    onApplyClick = {this.handleFilterApply}
    

    儿童活动电话

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

    更新(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 snapshot

    但问题本身就是误导

    Parent already knows Child’s 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} />;
      }
    });
    

    It becomes much clearer in this example that you already know what the props of Child are.

    JSBin snapshot

    如果它真的是关于使用儿童道具......

    如果它真的是关于使用Child的道具,你可以完全避免与Child的任何连接 .

    JSX有一个spread attributes 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 snapshot

    And there's no additional configuration required as you hookup additional Child components

    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 snapshot

    但我怀疑这不是你的实际用例 . 那么让我们进一步挖掘......

    一个强有力的实际例子

    所提供示例的一般性质很难谈 . 我创建了一个组件,演示了上述问题的实际用途,以一种非常复杂的方式实现:

    DTServiceCalculator working example
    DTServiceCalculator repo

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

    孩子们幸福无知

    ServiceItem是此示例中的子组件 . 它对外界没有太多意见 . 它requires a few props,其中一个是单击时要调用的函数 .

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

    它只会使用提供的 index [source]调用提供的 handleClick 回调 .

    父母是孩子

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

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

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

    handleServiceItem 捕获从子进程传递的索引,并将其提供给其父进程[source]

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

    业主知道一切

    该“所有权”的概念在React中是一个重要的概念 . 我建议阅读更多关于它here .

    在我已经展示的示例中,我继续将事件的处理委托给组件树,直到我们到达拥有该状态的组件 .

    当我们最终到达那里时,我们处理状态选择/取消选择,如此[source]:

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

    结论

    尽量保持最外层组件尽可能不透明 . 努力确保他们对父组件如何选择实现它们的偏好很少 .

    了解谁拥有您正在操纵的数据 . 在大多数情况下,您需要将树的事件处理委托给拥有该状态的组件 .

    旁白:Flux pattern是减少应用程序中此类必要连接的好方法 .

  • 8

    Edit :查看ES6更新示例的最终示例 .

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

    其他解决方案都缺少这一点

    虽然他们仍然工作正常,但其他答案缺少一些非常重要的答案 .

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

    The parent already has that child prop! :如果孩子有道具,那是因为它的父母为孩子提供了道具!你为什么要让孩子把道具传回父母,而父母显然已经拥有那个道具?

    更好的实施

    Child :它确实不必比那更复杂 .

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

    Parent with single 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 :您仍然拥有父母所需的一切,并且不需要让孩子更复杂 .

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

    Using props :正如我上面所解释的那样,你已经拥有了父级中的道具,因此将整个子组件传递给访问道具是没用的 .

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

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

    并访问父节点中的DOM节点

    React.findDOMNode(this.refs.theChild)
    

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

    该组件有一个接口(props),父节点不应该假设有关子内部工作的任何内容,包括它的内部DOM结构或它声明引用的DOM节点 . 使用儿童参考的父母意味着您紧密地耦合了2个组件 .

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

    他们在您,Web开发人员可以达到的范围和实施细节之间 Build 了界限,因此您无法访问 . 然而,浏览器可以随意跨越这个边界 . 有了这个边界,他们能够使用相同的古老的Web技术构建所有HTML元素,就像你想象的那样使用div和span .

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

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

    About using anything else

    在回调中传递整个组件是危险的,并且可能导致新手开发人员做一些非常奇怪的事情,例如调用 childComponent.setState(...)childComponent.forceUpdate() ,或在父母内部分配新变量,使得整个应用程序更难以推理 .


    Edit: ES6 examples

    现在许多人使用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):如果您使用 PureComponentshouldComponentUpdate ,则上述实施将不会默认情况下优化,因为在渲染阶段使用 onClick={e => doSomething()} 或直接绑定,因为每次父渲染时它都会创建一个新函数 . 如果这是您的应用程序中的性能瓶颈,您可以将数据传递给子项,并将其重新注入"stable"回调(在父类中设置,并绑定到类构造函数中的 this ),以便 PureComponent 优化可以启动,或者您可以实现自己的 shouldComponentUpdate 并忽略props比较检查中的回调 .

    您还可以使用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)
    

相关问题