Why does my ES6 (using Babel) class say `this` is undefined in an instance method?

后端 未结 1 739
既然无缘
既然无缘 2021-02-08 04:22

I am building an application in Node using Hapi.JS.

I have a class for an authentication plugin that is giving me all sorts of problems. When I attempt to reference

1条回答
  •  滥情空心
    2021-02-08 05:01

    ES6 class with babel doesn't autobind this for you. This is a common misconception since class was introduced. There are multiple ways to solve it.

    1. Use ES7. Babel has an experimental (as of this post) class-properties plugin.

      class OAuth {
        constructor () {}
      
        register = (server, err, next) => {
          this.server = server
          this.registerRoutes()
        }
      
        registerRoutes = () => {}
      }  
      

    How does this work? When you use arrow-functions along with the class-properties plugin, it gets transformed to something like the following, binding this as you expect when you use class syntax.

    var OAuth = function OAuth() {
      var _this = this;
    
      _classCallCheck(this, OAuth);
    
      this.register = function (server, err, next) {
        _this.server = server;
        _this.registerRoutes();
      };
    
      this.registerRoutes = function () {};
    }
    
    1. Bind your class properties in the constructor

      class OAuth {
        constructor () {
          // `this` is the OAuth instance in the constructor
          this.register = this.register.bind(this)
          this.registerRoutes = this.registerRoutes.bind(this)
        }
      
        register (server, err, next) {
          // `this` is the global object.. NOT! 
          // after binding in the constructor, it's the OAuth instance ^_^
          // provided you use `new` to initialize your instance
          this.server = server
          this.registerRoutes()
        }
      
        registerRoutes () {}
      }
      
    2. Use createClass from react, which does the binding for you. Note we're only using react for its class property binding magic. We are not creating react components.

      import React from 'react'
      
      const OAuth = React.createClass({
        register (server, err, next) {
          this.server = server
          this.registerRoutes()
        }
      
        registerRoutes () {}
      })
      
    3. Use only autoBind from react-class. Here we're making a react component using ES6+ class syntax just to use the autoBind method. We don't have to use componentWillMount, render, etc, which are provided with react components.

      import { autoBind } from 'react-class'
      
      class OAuth extends React.Component {
        constructor(props) {
          super(props)
          autoBind(this)
        }
      
        register (server, err, next) {
          this.server = server
          this.registerRoutes()
        }
      
        registerRoutes () {}
      }
      
    4. Roll your own class property binder. It's a nice exercise, basically the same as option 2, possibly less code as well.

      // call it in your constructor
      bindStuff(this, ['register', 'registerRoutes', 'etc'])
      
      // define it somewhere as
      function bindStuff (context, props) {
        props.forEach(prop => {
          context[prop] = context[prop].bind(context);
        })
      }
      
    5. If you actually want to create react components, you can combine arrow-functions and property initializers to do something like

      class OAuthComponent extends React.Component {
        whateverMethodYouWant = (event) => {
          this.setState({somePropertyYouCareAbout: true}) // this is bound
        }
      
        anotherMethod = () => {
          this.whateverMethodYouWant() // this is bound
        }
      }
      

    0 讨论(0)
提交回复
热议问题