React16 类组件函数中 this 绑定

89 阅读1分钟
  • 问题起因:

React16 中还是用类组件的方式,写了下面的一个 demo 产生了一个问题

<body>
  <div id="app"></div>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  <script type="text/babel">
    class App extends React.Component {
      constructor() {
        super();
        this.state = {
          message: "Hello World"
        };
      }
      render() {
        return (
          <div>
            <h2>{this.state.message}</h2>
            <button onClick={this.changeText}>改变文本</button>
          </div>
        )
      }
      changeText() {
        this.setState({
          message: "Hello React"
        })
      }
    }
    ReactDOM.render(<App />, document.getElementById("app"));
  </script>
</body>

此时点击按钮的时候,发现报错: Cannot read properties of undefined (reading 'setState') 其实就是 changeText 函数中的 this 丢失了。this 成了 undefined

  • 问题排查
  1. Javascript 函数中的 this 是谁去调用了函数 this 就指向谁。创建了组件实例 <App /> 所以 constructor 和 render 中的 this 是指向组件实例的。render 函数是实例创建好之后,通过实例对象调用的,所以this是指向实例对象的。
  2. changeText 函数赋值给了 button 的点击事件,监听点击,监听函数中的this本应该是节点对象,如下:
<body>
  <button id="create">创建</button>
  <script>
    var createBtn = document.getElementById('create')
    function add(){
      console.log(this) // <button id="create">创建</button>
    }
    createBtn.onclick = add 
  </script>
</body>
  1. 但是这里为什么是 undefined 呢,因为在类中开启了严格模式,,所以是 undefined,如下:
  <script type="text/babel">
      class App {
        show() {
          console.log(this);
        }
      }
 
      const app = new App();
      let temp = app.show;
      app.show(); // App {}: Object
      temp();     // undefined
  </script>

解决方式:

  1. bind (显示绑定 this)
    class App extends React.Component {
      constructor() {
        super();
        this.state = {
          message: "Hello World"
        };
        this.changeText = this.changeText.bind(this)
      }
      render() {
        return (
          <div>
            <h2>{this.state.message}</h2>
            <button onClick={this.changeText}>改变文本</button>
          </div>
        )
      }
      changeText() {
        this.setState({
          message: "Hello React"
        })
      }
    }
    ReactDOM.render(<App />, document.getElementById("app"));
  1. 箭头函数(changeText 函数中没有 this,用了类中的 this)
  • 箭头函数的 this 是外部作用域的 this ,与函数的调用方式无关。
  • 箭头函数的this无法通过 call、apply、bind 修改。
  • 箭头函数无法作为构造函数。
  <script type="text/babel">
    class App extends React.Component {
      constructor() {
        super();
        this.state = {
          message: "Hello World"
        };
      }
      render() {
        return (
          <div>
            <h2>{this.state.message}</h2>
            <button onClick={this.changeText}>改变文本</button>
          </div>
        )
      }
      changeText = () => {
        this.setState({
          message: "Hello React"
        })
      }
    }
    ReactDOM.render(<App />, document.getElementById("app"));
  </script>

3.  直接传入一个箭头函数

        btnClick() {
          console.log("btnClick", this);
          this.setState({ counter: 9999 });
        }
 
        render() {
          return <button onClick={() => this.btnClick()}>按钮3</button>;
        }

onClick 函数回调时,执行箭头函数,箭头函数再执行 btnClick 函数。this.btnClick()这里的 this 是组件实例对象。