事件处理:基于功能的组件与基于类的组件

Posted

技术标签:

【中文标题】事件处理:基于功能的组件与基于类的组件【英文标题】:Event handling: functional based component vs class based component 【发布时间】:2019-07-10 16:49:41 【问题描述】:

这是我的第一个 React 应用程序(react@16.8.1 )。我试图找出为什么 onClick 事件在使用基于功能的组件和基于类的组件时处理方式不同。

据我所知,我应该使用基于类的 c。只有当我需要改变它的状态时,对吗?

基于函数的组件抛出 _this is undefined error 但基于类 - 不是。

我在这两种情况下都使用箭头函数而不是绑定函数。

基于功能:

import React from 'react';

const AnswersDisplay = (props) => 
// even with bind I still get "_this is undefined"
//this.onAnswer = this.onAnswer.bind(this); 

  const answerList = props.answerList.map( (option) => 
    return (
      <button
        onClick=this.onAnswer
        value=option
        className="ui basic green button">option
      </button>
    )
  ); 

  const onAnswer = (e) =>
    console.log(e.target.value);
  

  return(
    <div className="ui two buttons hSpace">this.answerList</div>
  );
;

export default AnswersDisplay;

vs 基于类的方法。

import React from 'react';

class AnswersDisplay extends React.Component 
  constructor(props) 
    super(props);
    //this.onAnswer = this.onAnswer.bind(this);
  

  answerList = this.props.answerList.map( (option) => 
    return (
      <button
        onClick=this.onAnswer
        value=option
        className="ui basic green button">option
      </button>
    )
  );

  onAnswer = (e) =>
    console.log(e.target.value);
  

  render() 
    return(
        <div className="ui two buttons hSpace">this.answerList</div>
    );
  
;


export default AnswersDisplay;

【问题讨论】:

this 在函数组件和类组件中的工作方式不同,因此函数的行为也不同。检查 babel 是如何编译它们的 this 在函数组件中并不像在类组件中那样引用组件实例。请改用onClick=onAnswer 而不是onClick=this.onAnswer 使用onClick=onAnswer 谢谢大家的解释! @ElenaJdanova 对答案的支持会有所帮助 【参考方案1】:

MDN web doc 涵盖了您需要了解的有关 this 的所有信息。

为简单起见,请考虑objects,其中this 是“未定义”,除非:

方法/函数已使用 ES5 的 bind(this) 绑定到 object(请参阅下面的注释),或者通过使用 object 本身重新绑定外部函数来在 object 之外反弹:bind(obj)。李>

注意:如下所示(在方法 6 中),在 object 内部使用 ES5 arrow functions 意味着它将保留封闭的词法范围 this 而无需被束缚。

例如:

this.prop = "global prop"
const outsideArrowFunction = () => (this.prop)

function outsideFunction() 
    return this.prop;
;

const obj = 
  prop: "obj's prop",
  method: function() 
    return this.prop; // returns "obj's prop"
  ,
  method2: function() 
    return this; // returns the entire "obj" and its properties
  ,
  method3: function() 
    return this.method(); // returns "obj's prop"
  ,
  method4: function() 
    return outsideFunction(); // returns "global prop" because the outsideFunction's lexical scope doesn't recognize the "obj"'s nor its properties      
  ,
  method5: function() 
    return outsideArrowFunction(); // same as method4, utilizes global this
  ,
  method6: function() 
    const x = () => this.method();
    return x(); // returns "obj's prop" because arrow functions take on "this" from the "obj"
  ,
  method7: function() 
    const x = function()  
       return this.prop; 
    ;
    return x(); // returns "global prop" because "this" loses lexical scope upon execution
  ,
  method8: function() 
   const x = this.method.bind(this);
   return x(); // returns "obj's prop" because "this" refers to the "obj" upon execution
  ,
  method9: function(callback) 
    return callback(this.method);
  ,
  method10: function() 
    return this.method9(function(callback) 
      return callback(); // returns "global prop" because "this" loses lexical scope upon execution
    );
  
; 


const a = outsideArrowFunction.bind(obj); // returns "global prop" because arrow functions take on whatever "this" is upon its creation, so "this" refers to the global "this"
const b = outsideFunction.bind(obj); // returns "obj's prop" since a traditional function can rebind "this", which has been rebound to "obj"   
    
console.log(`Method: $obj.method()`);
console.log(`Method2: $obj.method2()`);
console.log(`Method3: $obj.method3()`);
console.log(`Method4: $obj.method4()`);
console.log(`Method5: $obj.method5()`);
console.log(`Method6: $obj.method6()`);
console.log(`Method7: $obj.method7()`);
console.log(`Method8: $obj.method8()`);
console.log(`Method10: $obj.method10()`);
console.log(`arrowFunction: $a()`);
console.log(`outsideFunction: $b()`);

对于classes,它们是objects 的模板。所以this 将是undefined 或全局this,除非method 类已绑定在constructor 中或您使用arrow function。通过单击每个按钮尝试下面的示例,注意所有 3 个方法可以如何工作,但它取决于它们的调用方式

class Example extends React.Component 
  constructor() 
    super();
    this.state =  method: "" ;
    this.boundMethod = this.boundMethod.bind(this);
  

  componentDidMount()  
    this.unboundMethod(); 
  ;

  boundMethod() 
    this.setState( method: "Bound Method" ); // this works because the method is bound to the class
  

  unboundMethod() 
    try 
      this.setState( method: "Unbound Method" ); // this only works if it's called within a bound method class (like componentDidMount)
     catch (err) 
      alert(err); // however, if it's called within a callback (like in an onClick event), it fails.
    
  

  arrowMethod = () => 
    this.setState( method: "Arrow Method" ); // this works because arrow methods are automatically bound to the class
  ;

  render() 
    return (
      <div>
        <button onClick=this.boundMethod>Bound Method</button>
        <button onClick=this.unboundMethod>Unbound Method</button>
        <button onClick=this.arrowMethod>Arrow Method</button>
        <p>The this.state.method was called</p>
     </div>
    );
  ;


ReactDOM.render(<Example />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

【讨论】:

【参考方案2】:

对于函数式组件,您声明一个常量,其中包含一个函数。如果单击按钮,您要调用它。但请确保在函数中使用this。在这种情况下,this 将引用全局执行上下文,并且在该特定上下文中,javascript 引擎将无法找到属性 onAnswer,因此它将返回 undefined

为了完成这项工作,您已经在没有this 的情况下回拨了电话。

像这样:onClick=onAnswer

整个代码如下所示:

import React from 'react';

const AnswersDisplay = (props) => 
// even with bind I still get "_this is undefined"
//this.onAnswer = this.onAnswer.bind(this); 

  const answerList = props.answerList.map( (option) => 
    return (
      <button
        onClick=onAnswer
        value=option
        className="ui basic green button">option
      </button>
    )
  ); 

  const onAnswer = (e) =>
    console.log(e.target.value);
  

  return(
    <div className="ui two buttons hSpace">this.answerList</div>
  );
;

【讨论】:

以上是关于事件处理:基于功能的组件与基于类的组件的主要内容,如果未能解决你的问题,请参考以下文章

Android学习笔记(36):Android的两种事件处理方式

源码解析Android中的事件处理

Java AWT 图形界面编程事件处理机制 ③ ( AWT 中常见的事件和事件监听器 | 低级事件 | 组件事件 | 窗口事件 | 鼠标事件 | 高级事件 | 动作事件 | 事件监听器 )

基于类的组件和 inChange 事件中的 function 关键字

Android零基础入门第33节:Android事件处理概述

基于监听的事件处理机制