无法访问事件处理程序中的React实例(this)

我在ES6(用BabelJS)写一个简单的组件,并且函数this.setState不起作用。

典型的错误包括像

无法读取未定义的属性“setState”

要么

this.setState不是一个函数

你知道为什么吗? 这里是代码:

 import React from 'react' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: 'startValue'} } sendContent(e) { console.log('sending input content '+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent}>Submit</button> </div> ) } } export default SomeClass 

this.changeContent需要在作为onChange prop传递之前通过this.changeContent.bind(this)绑定到组件实例,否则函数体中的thisvariables不会引用组件实例,而是引用到window 。 请参阅函数::绑定 。

当使用React.createClass而不是ES6类时,组件上定义的每个非生命周期方法都会自动绑定到组件实例。 请参阅自动绑定 。

请注意,绑定一个函数会创build一个新函数。 您可以直接在渲染中绑定它,这意味着每次组件渲染时都会创build一个新函数,或者在您的构造函数中绑定它,只会触发一次。

 constructor() { this.changeContent = this.changeContent.bind(this); } 

VS

 render() { return <input onChange={this.changeContent.bind(this)} />; } 

Refs在组件实例上设置,而不是在React.refs :您需要将React.refs.someref更改为this.refs.someref 。 您还需要将sendContent方法绑定到组件实例,以便引用它。

莫豪斯是正确的,但这可以解决没有bind

您可以将箭头function与类属性提议一起使用:

 class SomeClass extends React.Component { changeContent = (e) => { this.setState({inputContent: e.target.value}) } render() { return <input type="text" onChange={this.changeContent} />; } } 

由于箭头函数是在构造函数的范围内声明的,并且由于箭头函数从它们的声明范围中保留了this ,所以这一切都是可行的。 这里的缺点是这些不会是原型上的函数,它们都将被重新创build与每个组件。 然而,这并不是什么坏处,因为bind导致了相同的事情。

当从React.createClass()组件定义语法转换到扩展React.Component的ES6类方法时,这个问题是我们大多数人经历的第一件事情React.Component

这是由React.createClass()extends React.Componentthis上下文差异引起的

使用React.createClass()会自动绑定this上下文(值),但是这不是使用ES6类的情况。 当这样做ES6的方式(通过扩展React.Componentthis上下文默认为null 。 类的属性不会自动绑定到React类(组件)实例。


解决这个问题的方法

我总共知道4种一般方法。

  1. 在你的类的构造函数中绑定你的函数 。 被许多人认为是避免触及JSX的最佳实践方法,并且不会在每个组件重新渲染上创build新的function。

     class SomeClass extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } } 
  2. 在线绑定你的函数 。 你仍然可以在一些教程/文章/等等中find这种方法,所以重要的是你意识到这一点。 它像#1一样的概念,但要注意绑定一个函数为每个重新渲染创build一个新的函数。

     class SomeClass extends React.Component { handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick.bind(this)}></button> ); } } 
  3. 使用胖箭头function 。 直到箭头函数,每个新函数都定义了它自己的this值。 但是,箭头函数不会创build它自己的this上下文,所以this具有来自React组件实例的原始含义。 因此,我们可以:

     class SomeClass extends React.Component { handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={ () => this.handleClick() }></button> ); } } 

    要么

     class SomeClass extends React.Component { handleClick = () => { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } } 
  4. 使用实用函数库来自动绑定你的function 。 那里有几个实用程序库,它会自动为你做这项工作。 这里有一些stream行的,只是提到一些:

    • Autobind装饰器是一个NPM包,它将一个类的方法绑定到这个正确的实例上,即使这些方法是分离的。 该包在方法之前使用@autobind将其绑定到组件上下文的正确引用

       import autobind from 'autobind-decorator'; class SomeClass extends React.Component { @autobind handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } } 

      Autobind装饰器足够聪明,可以让我们像组件类一样绑定组件类中的所有方法,就像方法1一样。

    • Class Autobind是另一个被广泛用于解决这个绑定问题的NPM包。 与Autobind装饰器不同,它不使用装饰器模式,但实际上只是在构造函数中使用了一个函数该函数自动将 Component的方法绑定到这个的正确引用上。

       import autobind from 'class-autobind'; class SomeClass extends React.Component { constructor() { autobind(this); // or if you want to bind only only select functions: // autobind(this, 'handleClick'); } handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } } 

      PS:其他非常相似的库是React Autobind 。


build议

如果我是你,我会坚持方法#1。 但是,只要你在类的构造函数中得到了很多的绑定,我build议你去探索方法#4中提到的一个辅助类库。


其他

这与你的问题没有关系,但是你不应该过度使用文献 。

你的第一个倾向可能是在你的应用程序中使用引用“让事情发生”。 如果是这种情况,请花一点时间,更仔细地考虑组件层次结构中应该拥有哪些状态。

为了类似的目的,就像你需要的那样,使用受控组件是首选的方法。 我build议你考虑使用你的组件state 。 所以,你可以简单地访问这样的值: this.state.inputContent

我们需要将事件函数与构造函数中的组件绑定如下,

 import React from 'react' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: 'startValue'} this.changeContent = this.changeContent.bind(this); } sendContent(e) { console.log('sending input content '+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent}>Submit</button> </div> ) } } export default SomeClass 

谢谢

发生此问题是因为this.changeContentonClick={this.sendContent}未绑定到组件的实例。

还有另一种解决scheme(除了在构造函数()中使用bind())来使用ES6的箭头函数,它们共享周围代码的相同词法范围并维护 ,因此您可以将render()中的代码更改为是:

 render() { return ( <input type="text" onChange={ () => this.changeContent() } /> <button onClick={ () => this.sendContent() }>Submit</button> ) } 

你好,如果你不想在绑定自己的函数调用。 你可以使用'class-autobind'并像那样导入它

 import autobind from 'class-autobind'; class test extends Component { constructor(props){ super(props); autobind(this); } 

在超级调用之前不要写自动绑定,因为它不会工作

如果你想保持构造函数语法的绑定,你可以使用提议绑定运算符和转换你的代码如下:

 constructor() { this.changeContent = ::this.changeContent; } 

代替 :

 constructor() { this.changeContent = this.changeContent.bind(this); } 

简单得多,不需要bind(this)或者fatArrow

你可以通过三种方式解决这个问题

1.在构造函数中绑定事件函数如下

 import React from 'react' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: 'startValue'} this.changeContent = this.changeContent.bind(this); } sendContent(e) { console.log('sending input content '+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent}>Submit</button> </div> ) } } export default SomeClass 

绑定时被调用

 import React from 'react' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: 'startValue'} } sendContent(e) { console.log('sending input content '+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={this.sendContent.bind(this)}>Submit</button> </div> ) } } export default SomeClass 

3.使用箭头function

 import React from 'react' class SomeClass extends React.Component { constructor(props) { super(props) this.state = {inputContent: 'startValue'} } sendContent(e) { console.log('sending input content '+React.findDOMNode(React.refs.someref).value) } changeContent(e) { this.setState({inputContent: e.target.value}) } render() { return ( <div> <h4>The input form is here:</h4> Title: <input type="text" ref="someref" value={this.inputContent} onChange={this.changeContent} /> <button onClick={()=>this.sendContent()}>Submit</button> </div> ) } } export default SomeClass