在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的方式,也从来都不是一个好主意。我把答案换了。


当前回答

下面是一个简单的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>
    );
  }
}

下面是一个简单的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)}

问题是如何将参数从子组件传递给父组件。这个例子很容易使用和测试:

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

基本上,你使用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');
  }

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

编辑:ES6更新示例请参见结束示例。

这个答案简单地处理了直接亲子关系的情况。当父母和孩子可能有很多中介时,检查这个答案。

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

虽然它们仍然有效,但其他答案遗漏了一些非常重要的东西。

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

小提琴

带有子对象列表的父对象:您仍然拥有父对象上所需的所有内容,并且不需要使子对象更加复杂。

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

小提琴

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

注意,我们绑定的是一个空上下文,否则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)

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

组件有一个接口(props),父组件不应该假设子组件的内部工作,包括它的内部DOM结构或它声明引用的DOM节点。父组件使用子组件的ref意味着两个组件紧密耦合。

为了说明这个问题,我将引用Shadow DOM的这句话,它用于浏览器内部渲染滑块、滚动条、视频播放器等:

它们在您Web开发人员所能达到的范围之间创建了一个边界 以及所谓的实现细节,因此无法访问 你。然而,浏览器可以随意跨越这个边界。 有了这个边界,他们就能够构建所有HTML元素 使用相同的优秀的旧Web技术,脱离了div和span 就像你一样。

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

如果Chrome实现了滚动条,让客户端访问滚动条的内部dom节点,这意味着客户端可能有可能简单地打破滚动条,应用程序将更容易打破当Chrome执行自动更新后重构滚动条…相反,它们只允许访问一些安全的东西,比如使用CSS定制滚动条的某些部分。

关于使用其他东西

在回调中传递整个组件是很危险的,可能会导致新手开发人员做一些非常奇怪的事情,比如调用childComponent.setState(…)或childComponent.forceUpdate(),或者在父组件中为它分配新变量,使整个应用程序更加难以推理。


编辑:ES6示例

因为现在很多人都在使用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>
)

小提琴


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.

你也可以使用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)

在这种情况下,你可以优化子组件使用:

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