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

我知道你可以使用受控组件来传递输入的值,但最好是传递整个套件。有时子组件包含一组您不愿意查找的信息。

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

更新 – 9/1/2015

在使用React一年多之后,在Sebastien Lorber的回答的刺激下,我得出结论,将子组件作为参数传递给父函数实际上不是React的方式,也从来都不是一个好主意。我把答案换了。


当前回答

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

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

是的,这是可能的。

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

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快照

但这个问题本身具有误导性

父母已经知道孩子的道具。

这在所提供的示例中并不清楚,因为实际上没有提供任何道具。这个示例代码可能更好地支持所提出的问题:

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

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

JSBin快照

如果真的是用孩子的道具……

如果它真的是关于使用一个孩子的道具,你可以完全避免与孩子的任何联系。

JSX有一个扩展属性API,我经常在Child等组件上使用。它获取所有的道具并将它们应用到一个组件上。孩子看起来是这样的:

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

允许你直接在父对象中使用这些值:

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快照

当您连接额外的子组件时,不需要额外的配置

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快照

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

一个健壮的实际示例

所提供示例的一般性质很难讨论。我已经创建了一个组件来演示上面问题的实际用途,以非常react的方式实现:

DTServiceCalculator工作示例 DTServiceCalculator回购

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

孩子们是幸福的无知

ServiceItem是本例中的子组件。它对外界没有太多看法。它需要一些道具,其中之一是单击时要调用的函数。

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

它只使用所提供的索引[source]调用所提供的handleClick回调函数。

父母就是孩子

DTServicesCalculator是这个例子中的父组件。它也是一个孩子。让我们看。

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

< serserceitem chosen={\ id}索引价格={\价格}handleServiceItem} - >

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

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

主人无所不知

“所有权”的概念在React中很重要。我建议在这里阅读更多相关内容。

在我所展示的示例中,我一直将事件的处理委托给组件树,直到我们到达拥有状态的组件。

当我们最终到达那里时,我们像这样处理状态选择/取消选择:

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

结论

试着让你最外层的组件尽可能不透明。努力确保它们对于父组件可能选择如何实现它们的偏好很少。

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

旁白:Flux模式是减少应用中这种必要连接的好方法。

其他回答

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

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

确保在父构造函数中绑定了在父构造函数中创建的函数 将绑定函数作为prop向下传递给子函数(没有lambda,因为我们将引用传递给函数) 从子事件调用绑定函数(Lambda!我们在事件触发时调用函数。 如果我们不这样做,函数将在加载时自动运行,而不会在事件时被触发。)

父函数

handleFilterApply(filterVals){} 

父类构造函数

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

传给孩子的道具

onApplyClick = {this.handleFilterApply}

子事件调用

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

这是一个没有使用onClick事件的例子。我只是通过props将一个回调函数传递给子函数。通过那个回调,子调用也发送回数据。我受到了文档中的例子的启发。

小例子(这是在tsx文件中,所以道具和状态必须完全声明,我删除了组件中的一些逻辑,所以代码更少)。

*更新:重要的是将此绑定到回调,否则回调的范围是子函数而不是父函数。唯一的问题是:这是“老”父母…

症候选择器是父对象:

interface SymptomChooserState {
  // true when a symptom was pressed can now add more detail
  isInDetailMode: boolean
  // since when user has this symptoms
  sinceDate: Date,
}

class SymptomChooser extends Component<{}, SymptomChooserState> {

  state = {
    isInDetailMode: false,
    sinceDate: new Date()
  }

  helloParent(symptom: Symptom) {
    console.log("This is parent of: ", symptom.props.name);
    // TODO enable detail mode
  }

  render() {
    return (
      <View>
        <Symptom name='Fieber' callback={this.helloParent.bind(this)} />
      </View>
    );
  }
}

Symptom是child(在child的props中我声明了回调函数,在函数selectedSymptom中调用了回调函数):

interface SymptomProps {
  // name of the symptom
  name: string,
  // callback to notify SymptomChooser about selected Symptom.
  callback: (symptom: Symptom) => void
}

class Symptom extends Component<SymptomProps, SymptomState>{

  state = {
    isSelected: false,
    severity: 0
  }

  selectedSymptom() {
    this.setState({ isSelected: true });
    this.props.callback(this);
  }

  render() {
    return (
      // symptom is not selected
      <Button
        style={[AppStyle.button]}
        onPress={this.selectedSymptom.bind(this)}>
        <Text style={[AppStyle.textButton]}>{this.props.name}</Text>
      </Button>
    );
  }
}

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

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

是的,这是可能的。

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

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快照

但这个问题本身具有误导性

父母已经知道孩子的道具。

这在所提供的示例中并不清楚,因为实际上没有提供任何道具。这个示例代码可能更好地支持所提出的问题:

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

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

JSBin快照

如果真的是用孩子的道具……

如果它真的是关于使用一个孩子的道具,你可以完全避免与孩子的任何联系。

JSX有一个扩展属性API,我经常在Child等组件上使用。它获取所有的道具并将它们应用到一个组件上。孩子看起来是这样的:

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

允许你直接在父对象中使用这些值:

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快照

当您连接额外的子组件时,不需要额外的配置

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快照

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

一个健壮的实际示例

所提供示例的一般性质很难讨论。我已经创建了一个组件来演示上面问题的实际用途,以非常react的方式实现:

DTServiceCalculator工作示例 DTServiceCalculator回购

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

孩子们是幸福的无知

ServiceItem是本例中的子组件。它对外界没有太多看法。它需要一些道具,其中之一是单击时要调用的函数。

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

它只使用所提供的索引[source]调用所提供的handleClick回调函数。

父母就是孩子

DTServicesCalculator是这个例子中的父组件。它也是一个孩子。让我们看。

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

< serserceitem chosen={\ id}索引价格={\价格}handleServiceItem} - >

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

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

主人无所不知

“所有权”的概念在React中很重要。我建议在这里阅读更多相关内容。

在我所展示的示例中,我一直将事件的处理委托给组件树,直到我们到达拥有状态的组件。

当我们最终到达那里时,我们像这样处理状态选择/取消选择:

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

结论

试着让你最外层的组件尽可能不透明。努力确保它们对于父组件可能选择如何实现它们的偏好很少。

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

旁白:Flux模式是减少应用中这种必要连接的好方法。

基本上,你使用props向子节点和父节点发送信息。

除了这些精彩的答案之外,让我举一个简单的例子来解释在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');
  }

这就是如何将值推入从客户机到服务器的道具中

答案似乎很简单。考虑一下:

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函数接受参数组件和事件。我认为这是最好的选择。

更新:9/1/2015

这是一个坏主意:让子实现细节泄露到父实现从来都不是一个好方法。见塞巴斯蒂安·洛伯的回答。