React:组件函数内的“this”未定义

220
class PlayerControls extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      loopActive: false,
      shuffleActive: false,
    }
  }

  render() {
    var shuffleClassName = this.state.toggleActive ? "player-control-icon active" : "player-control-icon"

    return (
      <div className="player-controls">
        <FontAwesome
          className="player-control-icon"
          name='refresh'
          onClick={this.onToggleLoop}
          spin={this.state.loopActive}
        />
        <FontAwesome
          className={shuffleClassName}
          name='random'
          onClick={this.onToggleShuffle}
        />
      </div>
    );
  }

  onToggleLoop(event) {
    // "this is undefined??" <--- here
    this.setState({loopActive: !this.state.loopActive})
    this.props.onToggleLoop()
  }

我想要在切换时更新loopActive状态,但处理程序中的this对象未定义。根据教程文档,this应该引用组件。我是否漏掉了什么?

12个回答

281

ES6 React.Component 不会自动将方法绑定到组件本身。您需要在 constructor 中手动绑定它们。像这样:

constructor (props){
  super(props);
  
  this.state = {
      loopActive: false,
      shuffleActive: false,
    };
  
  this.onToggleLoop = this.onToggleLoop.bind(this);

}

38
如果您将 onClick 属性更改为 () => this.onToggleLoop,并在将 onToggleLoop 函数移动到 React 类中后,它也可以正常工作。 - Sam
91
你真的需要绑定每个React类的每个方法吗?那不是有点疯狂吗? - Alex L
7
@AlexL,有一些方法可以在不显式绑定方法的情况下实现。如果你使用babel,可以将React组件上的每个方法声明为箭头函数。这里提供了一些示例:https://babeljs.io/blog/2015/06/07/react-on-es6-plus - Ivan
12
为什么 this 在这里一开始是未定义的呢?我知道在JavaScript中,this 取决于函数的调用方式,但是这里发生了什么事情? - darKnight
4
文章简要概述:使用箭头函数代替其他函数。 - OscarRyz
显示剩余5条评论

109

有几种方法。

一种是在构造函数中添加this.onToggleLoop = this.onToggleLoop.bind(this);

另一种方法是使用箭头函数onToggleLoop = (event) => {...}

还有一种是onClick={this.onToggleLoop.bind(this)}


4
为什么 onToogleLoop = () => {} 能够工作?我遇到了相同的问题,我在构造函数中绑定了它,但是它没有起作用... 现在我看到了你的帖子,用箭头函数语法替换了我的方法,它可以工作。你能解释一下吗? - Guchelkaben
8
箭头函数不会创建自己的 this,而是使用封闭执行上下文的 this 值。 - J. Mark Stevens
3
请注意,在 onClick 中绑定的内联函数将在每次渲染时返回一个新的函数,因此它看起来像是传递了一个新值给属性,会影响到 PureComponent 中的 shouldComponentUpdate - Ninjakannon

37

以这种方式编写您的函数:

onToggleLoop = (event) => {
    this.setState({loopActive: !this.state.loopActive})
    this.props.onToggleLoop()
}

Fat Arrow Functions

与使用 function 声明的函数不同,使用箭头函数声明的函数中 this 关键字的绑定在函数内外相同。这很方便地保留了this绑定,例如在操作如映射(mapping)时:this.items.map(x => this.doSomethingWith(x))。


如果我这样做,会出现“ReferenceError:fields are not currently supported”的错误。 - Pavel Komarov
1
如果我在构造函数内部说this.func =()=> {...},它可以工作,但我认为这有点傻,并且如果可能的话要避免它。 - Pavel Komarov
2
太糟糕了,无法在React中使用正常的类语法! - Kokodoko

12

我在一个渲染函数中遇到了类似的困境,最终采用以下方式传递了this的上下文:

{someList.map(function(listItem) {
  // your code
}, this)}

我也使用过:

{someList.map((listItem, index) =>
    <div onClick={this.someFunction.bind(this, listItem)} />
)}

1
你创建了很多不必要的函数,每次渲染列表时都会执行... - T.J. Crowder
@T.J.Crowder 是的,这些函数每次调用渲染时都会重新创建。最好将这些函数创建为类方法,并将它们绑定到类上一次,但对于初学者来说,手动上下文绑定可能会有所帮助。 - duhaime

4
在我的情况下,这是解决方案 = () => {}
methodName = (params) => {
//your code here with this.something
}

这是真正的解决方案。我忘记将其放在一个导致错误的函数中。 - Nathan Gouy

3

请注意:this 取决于函数被调用的方式。例如:当一个函数作为对象的方法被调用时,this 将会被设置为该方法所在的对象。

this 在 JSX 上下文中可作为组件对象访问,所以您可以将所需方法称之为内联的 this 方法。

如果你只是传递了函数/方法的引用,则 React 似乎会将其作为独立函数调用。

onClick={this.onToggleLoop} // Here you just passing reference, React will invoke it as independent function and this will be undefined

onClick={()=>this.onToggleLoop()} // Here you invoking your desired function as method of this, and this in that function will be set to object from that function is called ie: your component object

1
没错,你甚至可以使用第一行代码,即 onClick={this.onToggleLoop}。前提是在你的组件类中定义了一个字段(属性)onToggleLoop = () => /*使用 'this' 的代码体*/ - gvlax

1
我想解释一下为什么this是未定义的:
如果我们在非箭头函数中使用this,当不在严格模式下时,this将绑定到全局对象。但是在严格模式下,this将是未定义的(https://www.w3schools.com/js/js_this.asp)。
而ES6模块始终处于严格模式下(javascript: use strict is unnecessary inside of modules)。
您可以在构造函数中使用bind方法将this绑定到PlayerControls组件的实例中,以在onToggleLoop函数中绑定this
constructor(props) {
    super(props)

    this.state = {
      loopActive: false,
      shuffleActive: false,
    }

    this.onToggleLoop = this.onToggleLoop.bind(this)
}

或者使用箭头函数:

onToggleLoop = (event) => {
    this.setState({loopActive: !this.state.loopActive})
    this.props.onToggleLoop()
}

箭头函数没有上下文,因此箭头函数中的 this 将代表定义箭头函数的对象。

1
可以添加一些关于绑定上下文的内容。在JS中,this的概念可能会非常令人困惑,通常需要更多的说明。 - Lee

1
如果您使用babel,您可以使用ES7绑定运算符来绑定'this' https://babeljs.io/docs/en/babel-plugin-transform-function-bind#auto-self-binding
export default class SignupPage extends React.Component {
  constructor(props) {
    super(props);
  }

  handleSubmit(e) {
    e.preventDefault(); 

    const data = { 
      email: this.refs.email.value,
    } 
  }

  render() {

    const {errors} = this.props;

    return (
      <div className="view-container registrations new">
        <main>
          <form id="sign_up_form" onSubmit={::this.handleSubmit}>
            <div className="field">
              <input ref="email" id="user_email" type="email" placeholder="Email"  />
            </div>
            <div className="field">
              <input ref="password" id="user_password" type="new-password" placeholder="Password"  />
            </div>
            <button type="submit">Sign up</button>
          </form>
        </main>
      </div>
    )
  }

}

0
如果您在组件的生命周期方法(如componentDidMount)中调用已创建的方法,则只能使用this.onToggleLoop = this.onToogleLoop.bind(this)和箭头函数onToggleLoop = (event) => {...}
在构造函数中声明函数的常规方法不起作用,因为生命周期方法先被调用。

0

你可以重写你的 onToggleLoop 方法在 render() 方法中的调用方式。

render() {
    var shuffleClassName = this.state.toggleActive ? "player-control-icon active" : "player-control-icon"

return (
  <div className="player-controls">
    <FontAwesome
      className="player-control-icon"
      name='refresh'
      onClick={(event) => this.onToggleLoop(event)}
      spin={this.state.loopActive}
    />       
  </div>
    );
  }

React文档展示了在属性表达式中调用函数的模式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接