亲宝软件园·资讯

展开

react报错 TypeError: Cannot read property 'setState' of undefined

漫思 人气:0

代码如下:

  1.  
    class test extends Component {
  2.  
    constructor(props) {
  3.  
    super(props);
  4.  
    this.state = {
  5.  
    liked: false
  6.  
    };
  7.  
    }
  8.  
    handleClick(event) {
  9.  
    this.setState({liked: !this.state.liked});
  10.  
    }
  11.  
    render() {
  12.  
    var text = this.state.liked ? '喜欢' : '不喜欢';
  13.  
    return (
  14.  
    <div onClick={this.handleClick}>
  15.  
    你<b>{text}</b>我。点我切换状态。
  16.  
    </div>
  17.  
    );
  18.  
    }
  19.  
     
  20.  
    }
  21.  
    export default test;

可以正常展示页面:

但是按钮一按就会报错。

为什么会出现这种情况呢?

因为点击按钮时,到了handleClick()方法中的this已经不是组件里的this了。

第一种解决方法是:手动绑定this。将

  1.  
    constructor(props) {
  2.  
    super(props);
  3.  
    this.state = {
  4.  
    liked: false
  5.  
    };
  6.  
    }

改为

  1.  
    constructor(props) {
  2.  
    super(props);
  3.  
    this.state = {
  4.  
    liked: false
  5.  
    };
  6.  
    this.handleClick = this.handleClick.bind(this);//手动绑定
  7.  
    }

第二种解决办法是:将

  1.  
    handleClick(event) {
  2.  
    this.setState({liked: !this.state.liked});
  3.  
    }

改为

  1.  
    handleClick= (e) => {
  2.  
    this.setState({liked: !this.state.liked});
  3.  
    }

这种解决方法之所以能解决问题,就引申到了另外一个问题:函数作为React组件的方法时, 箭头函数和普通函数的区别是什么?

举个例子:下面2个a的定义有什么区别?

  1.  
    class App extends Component {
  2.  
    a() {
  3.  
    console.log(1)
  4.  
    }
  5.  
     
  6.  
    a = () => {
  7.  
    console.log(1)
  8.  
    }
  9.  
    }

第一个 a 不必说,是原型方法的定义。宽松模式下对应 ES5 就是

App.prototype.a = function() {}

第二个是 Stage 2 Public Class Fields 里面的写法,babel 下需要用 Class properties transform Plugin 进行转义。相当于:

  1.  
    class App extends Component {
  2.  
    constructor (...args) {
  3.  
    super(...args)
  4.  
    this.a = () => {
  5.  
    console.log(1)
  6.  
    }
  7.  
    }
  8.  
    }

为什么需要第二种写法?

在 React 里面,要将类的原型方法通过 props 传给子组件,传统写法需要 bind(this),否则方法执行时 this 会找不到:

<button onClick={this.handleClick.bind(this)}></button>

或者

<button onClick={(e) => this.handleClick(e)}></button>

这种写法难看不说,还会对 React 组件的 shouldComponentUpdate 优化造成影响。

这是因为 React 提供了 shouldComponentUpdate 让开发者能够控制避免不必要的 render,还提供了在 shouldComponentUpdate 自动进行 Shallow Compare 的 React.PureComponent, 继承自 PureComponent 的组件只要 props 和 state 中的值不变,组件就不会重新 render。

然而如果用了 bind this,每次父组件渲染,传给子组件的 props.onClick 都会变,PureComponent 的 Shallow Compare 基本上就失效了,除非你手动实现 shouldComponentUpdate.

使用 Public Class Fields 的这种写法,就解决了这个问题。另外还有其他若干种办法,比如先定义原型方法,然后在 constructor 里面 bind 一遍;或者使用 decorator 进行 bind 等:

  1.  
    class A {
  2.  
    constructor() {
  3.  
    this.a = this.a.bind(this)
  4.  
    }
  5.  
     
  6.  
    a() {}
  7.  
     
  8.  
    // or
  9.  
    @bindthis
  10.  
    b() {}
  11.  
    }

而箭头函数除了代码少。与普通函数最大的不同就是:this是由声明该函数时候定义的,一般是隐性定义为声明该函数时的作用域this。

  1.  
    var a = ()=>{
  2.  
    console.log(this)
  3.  
    }
  4.  
    //等同于
  5.  
    var a = function(){
  6.  
    console.log(this)
  7.  
    }.bind(this);
  8.  
     
  9.  
    a(); //Window
  10.  
    var b = function(){
  11.  
    console.log(this)
  12.  
    };
  13.  
    b(); //Window
  14.  
    var obj = { a,b };
  15.  
    obj.a(); //Window
  16.  
    obj.b(); //obj
 
箭头函数最大的作用是使得this从正常情况下的动态作用域(根据运行位置确定值)变成了静态作用域(根据定义位置确定值,也就是词法作用域)。
若想了解得更详细,可以去阅读官方文档: https://reactjs.org/docs/handling-events.html

加载全部内容

相关教程
猜你喜欢
用户评论