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

Ken*_*llB 276 javascript reactjs

propsReact.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} />
  }
});
Run Code Online (Sandbox Code Playgroud)

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

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

更新 - 2015年9月1日

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

Seb*_*ber 255

编辑:请参阅ES6更新示例的最终示例.

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

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

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

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

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

更好的实施

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

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

有单个孩子的父母:使用它传递给孩子的值

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);
  }
});
Run Code Online (Sandbox Code Playgroud)

的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);
  }
});
Run Code Online (Sandbox Code Playgroud)

的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} />
  }
});
Run Code Online (Sandbox Code Playgroud)

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

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

<Child ref="theChild" .../>
Run Code Online (Sandbox Code Playgroud)

并访问父节点中的DOM节点

React.findDOMNode(this.refs.theChild)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

关于使用其他任何东西

传递回调全体成分是危险的,可能会导致初级开发人员做的非常奇怪的事情就像调用childComponent.setState(...)childComponent.forceUpdate(),或分配给它新的变量,父里面,使得整个应用程序更难推理.


编辑:ES6示例

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

孩子可以很简单:

const Child = ({
  onClick, 
  text
}) => (
  <button onClick={onClick}>
    {text}
  </button>
)
Run Code Online (Sandbox Code Playgroud)

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

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>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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>
)
Run Code Online (Sandbox Code Playgroud)

的jsfiddle


PERF WARNING(适用于ES5/ES6):如果您正在使用PureComponentshouldComponentUpdate,默认情况下不会优化上述实现onClick={e => doSomething()},因为在渲染阶段直接使用或绑定,因为每次父渲染时它都会创建一个新函数.如果这是您的应用程序中的性能瓶颈,您可以将数据传递给子项,并将其重新注入"稳定"回调(在父类上设置,并绑定到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)
Run Code Online (Sandbox Code Playgroud)

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

const OptimizedChild = onlyUpdateForKeys(['text'])(Child)
Run Code Online (Sandbox Code Playgroud)

  • 是的,去年我一直在使用 React,我同意——没有充分的理由通过函数传递实例化组件。我将否决我自己的答案。我也愿意改变答案,也许会进行一些修改:您的“更好的实施”并没有解决这个问题出现的实际原因,即“我如何知道我的哪个孩子被选为群体中的哪个?” Flux 是任何大小合适的应用程序的答案,但是,在小型应用程序中,我最好通过“props”上的函数传递值(而不是组件)来备份调用链。同意? (2认同)

cha*_*tic 143

更新(2015年1月9日):OP使这个问题成为一个移动目标.它已经再次更新.所以,我觉得有责任更新我的回复.

首先,回答您提供的示例:

是的,这是可能的.

你可以通过更新Child's 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>;
  }
});
Run Code Online (Sandbox Code Playgroud)

然后,Parent中的事件处理程序可以访问组件和事件,如下所示:

  onClick: function (component, event) {
    // console.log(component, event);
  },
Run Code Online (Sandbox Code Playgroud)

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} />;
  }
});
Run Code Online (Sandbox Code Playgroud)

在这个例子中,你已经知道Child的道具是什么,这一点变得更加清晰.

JSBin快照


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

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

JSX有一个扩展属性 API,我经常在像Child这样的组件上使用它.它需要所有props并将它们应用于组件.孩子看起来像这样:

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

允许您直接在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} />;
  }
});
Run Code Online (Sandbox Code Playgroud)

JSBin快照


当您连接其他子组件时,不需要其他配置

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>;
  }
});
Run Code Online (Sandbox Code Playgroud)

JSBin快照

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


一个强大的实际例子

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

DTServiceCalculator工作示例
DTServiceCalculator repo

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

孩子们幸福无知

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

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

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

父母是孩子

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

DTServiceCalculator创建一个子组件列表,ServiceItem并为它们提供道具[ 来源 ].它是父组件,ServiceItem但它是传递列表的组件的子组件.它不拥有数据.因此,它再次将组件的处理委托给其父组件

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

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

handleServiceClick (index) {
  this.props.onSelect(index);
}
Run Code Online (Sandbox Code Playgroud)

业主知道一切

"所有权"的概念在React中是一个重要的概念.我建议在这里阅读更多相关内容.

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

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

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


结论

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

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

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

  • 谢谢@Pathsofdesign!这要看情况。Flux 有控制器视图的概念。在这个例子中,“Parent”可能是这样一个控制器视图,而“Child”只是一个哑视图(组件)。只有控制器视图才应该了解应用程序。在这种情况下,您仍然需要将 Action 作为 prop 从 `Parent` 传递给 `Child`。就 Action 本身而言,Flux 对于 Action 之间的交互以更新 View 有一个严格规定的模式。http://facebook.github.io/flux/docs/overview.html#structure-and-data-flow (2认同)
  • `onClick={this.onClick.bind(null, this.state.text)}` 不需要绑定状态,因为父级有。所以只需 `onClick={this.onClick}` 就可以了。其中父级中的`onClick = ()=&gt;{const text = this.state.text;..}` (2认同)

Ken*_*llB 25

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

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} />
  }
});
Run Code Online (Sandbox Code Playgroud)

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

更新:2015年9月1日

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

  • @TheSurrican 不,完全没问题。第一个参数绑定到函数内的“this”(无论如何都不需要),第二个参数在调用 onClick 时作为第一个参数添加到前面。 (2认同)

Rom*_*man 9

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

//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")
    );
}
Run Code Online (Sandbox Code Playgroud)

看看JSFIDDLE


Ign*_*rew 6

基本上,您使用道具向Child和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>
      );
   }
}
Run Code Online (Sandbox Code Playgroud)

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>

      );
   }
}
Run Code Online (Sandbox Code Playgroud)

Main.js

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App.jsx';

ReactDOM.render(<App />, document.getElementById('app'));
Run Code Online (Sandbox Code Playgroud)

多数民众赞成,现在您可以将值从客户端传递到服务器.

看一下Header.js中的Change函数

Change(event) {
      // this.props.change(this.state.names);
      this.props.change('jessy');
  }
Run Code Online (Sandbox Code Playgroud)

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