无法访问React实例(此)内部事件处理程序

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

典型的错误包括

无法读取undefined的属性“setState”

这一点。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
86288 次浏览

this.changeContent需要在作为onChange道具传递之前通过this.changeContent.bind(this)绑定到组件实例,否则函数体中的this变量将不会指向组件实例,而是指向window。看到功能::绑定

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

注意绑定一个函数会创建一个新函数。您可以直接在呈现中绑定它,这意味着每次组件呈现时都会创建一个新函数,也可以在构造函数中绑定它,这只会触发一次。

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

vs

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

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

Morhaus是正确的,但这可以在没有bind的情况下解决。

你可以将箭头功能类属性提案一起使用:

class SomeClass extends React.Component {
changeContent = (e) => {
this.setState({inputContent: e.target.value})
}


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

因为箭头函数是在构造函数的作用域中声明的,并且因为箭头函数在其声明的作用域中维护this,所以它都可以工作。这里的缺点是这些不会是原型上的函数,它们都将与每个组件一起重新创建。然而,这并不是一个很大的缺点,因为bind会导致同样的结果。

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

还有另一种解决方案(除了在构造函数()中使用bind()之外)使用ES6中的箭头函数,这些函数与周围的代码共享相同的词法范围并维护,因此你可以在render()中将代码更改为:

render() {
return (


<input type="text"
onChange={ () => this.changeContent() } />


<button onClick={ () => this.sendContent() }>Submit</button>


)
}

这个问题是我们大多数人在从React.createClass()组件定义语法过渡到扩展React.Component的ES6类方式时最先经历的事情之一。

这是由__ABC1和__ABC2中的this上下文差异引起的

使用React.createClass()将自动正确绑定this上下文(值),但在使用ES6类时并非如此。在ES6中(通过扩展React.Component), this上下文默认为null。类的属性不会自动绑定到React类(组件)实例。


解决该问题的方法

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

  1. < p > 在类构造函数中绑定函数。许多人认为这是一种最佳实践方法,它完全避免接触JSX,并且不会在每个组件上重新渲染时创建新函数。

    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. Bind your functions inline. You can still find this approach used here and there in some tutorials / articles / etc, so it's important you're aware of it. It it the same concept like #1, but be aware that binding a function creates a new function per each re-render.

    class SomeClass extends React.Component {
    handleClick() {
    console.log(this); // the React Component instance
    }
    render() {
    return (
    <button onClick={this.handleClick.bind(this)}></button>
    );
    }
    }
    
  3. Use a fat arrow function. Until arrow functions, every new function defined its own this value. However, the arrow function does not create its own this context, so this has the original meaning from the React component instance. Therefore, we can:

    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. Use utility function library to automatically bind your functions. There are a few utility libraries out there, that automatically does the job for you. Here are some of the popular, just to mention a few:

    • Autobind Decorator is an NPM package which binds methods of a class to the correct instance of this, even when the methods are detached. The package uses @autobind before methods to bind this to the correct reference to the component's context.

      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 Decorator足够聪明,可以让我们一次绑定组件类中的所有方法,就像方法#1一样

    • 类Autobind是另一个广泛用于解决此绑定问题的NPM包。与Autobind Decorator不同,它不使用装饰器模式,而是真正使用只需在构造函数中使用自动绑定的函数组件的方法来正确引用this

      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:另一个非常相似的库是反应Autobind.

    • 李< / ul > < / >

建议

如果我是你,我会坚持第一种方法。但是,一旦您的类构造函数中有大量的绑定,我建议您研究方法#4中提到的helper库之一。


其他

这与你的问题无关,但你不应该过度使用裁判

你的第一个倾向可能是使用refs在你的应用中“让事情发生”。如果是这种情况,花点时间,更批判性地思考在组件层次结构中应该在哪里拥有状态。

出于类似的目的,就像你需要的一样,使用控制组件是首选的方式。我建议你考虑使用你的组件state。所以,你可以像这样访问这个值:this.state.inputContent

你好,如果你不想关心绑定你自己的函数调用。你可以使用'class-autobind'然后像这样导入它

import autobind from 'class-autobind';


class test extends Component {
constructor(props){
super(props);
autobind(this);
}

不要在super调用之前写autobind,因为它将不起作用

如果你想在构造函数语法中保留bind,你可以使用proposal-bind-operator并像下面这样转换你的代码:

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

而不是:

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

更简单,不需要bind(this)fatArrow

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

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

谢谢

你可以用三种方法来解决这个问题

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

2.在调用时绑定

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.通过使用箭头函数

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

我的建议是使用箭头函数作为属性

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>
);
}
}

因为第二种方法每次渲染调用都会生成新函数,实际上这意味着新指针和新版本的props,如果你以后关心性能,你可以使用反应。PureComponent或在反应。组件中,你可以覆盖shouldComponentUpdate (nextProps nextState)并在props到达时进行浅检查

此问题发生在react15.0之后,该事件处理程序没有自动绑定到组件。因此,无论何时调用事件处理程序,都必须手动将其绑定到组件。


有几种方法可以解决这个问题。但是你需要知道哪种方法是最好的,为什么?通常,我们建议将函数绑定到类构造函数中,或者使用箭头函数。

// method 1: use a arrow function
class ComponentA extends React.Component {
eventHandler = () => {
console.log(this)
}
render() {
return (
<ChildComponent onClick={this.eventHandler} />
);
}


// method 2: Bind your functions in the class constructor.
class ComponentA extends React.Component {
constructor(props) {
super(props);
this.eventHandler = this.eventHandler.bind(this);
}
render() {
return (
<ChildComponent onClick={this.eventHandler} />
);
}

这两个方法不会在组件每次渲染时创建一个新函数。所以我们的ChildComponent将不会因为新的函数道具的改变而重新渲染,或者可能会产生性能问题。

您可以按照以下步骤解决这个问题

用更改sendContent函数

 sendContent(e) {
console.log('sending input content '+this.refs.someref.value)
}

使用以下命令更改呈现函数

<input type="text" ref="someref" value={this.state.inputContent}
onChange={(event)=>this.changeContent(event)} />
<button onClick={(event)=>this.sendContent(event)}>Submit</button>

我们必须用this bind函数来获得类中的函数实例。举个例子

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

这样this.state将是有效的对象。

虽然前面的回答已经提供了解决方案的基本概述(即绑定,箭头函数,为你做这件事的装饰器),我还没有遇到一个答案,实际上解释了为什么这是必要的——在我看来,这是混乱的根源,并导致不必要的步骤,如不必要的重新绑定和盲目地跟随别人做什么。

this是动态的

为了理解这种具体情况,简要介绍this是如何工作的。这里的关键是this是一个运行时绑定,依赖于当前的执行上下文。因此,为什么它通常被称为“上下文”——提供当前执行上下文的信息,以及为什么需要绑定是因为您失去了“上下文”。但让我用一个片段来说明这个问题:

const foobar = {
bar: function () {
return this.foo;
},
foo: 3,
};
console.log(foobar.bar()); // 3, all is good!

在这个例子中,我们得到了3,正如预期的那样。举个例子:

const barFunc = foobar.bar;
console.log(barFunc()); // Uh oh, undefined!

可能会意外地发现它的日志是未定义的——3去哪里了?答案在于“背景”,或者你如何执行一个函数。比较一下我们如何调用函数:

// Example 1
foobar.bar();
// Example 2
const barFunc = foobar.bar;
barFunc();

注意区别。在第一个例子中,我们精确地指定了bar方法__abc2的位置——在foobar对象上:

foobar.bar();
^^^^^^

但在第二种方法中,我们将方法存储到一个新变量中,并使用该变量来调用该方法,而不显式地声明该方法实际存在的位置,因此失去了背景:

barFunc(); // Which object is this function coming from?

问题就在这里,当你在变量中存储一个方法时,关于该方法所在位置的原始信息(该方法正在执行的上下文)就丢失了。如果没有这些信息,在运行时,JavaScript解释器就无法绑定正确的this -如果没有特定的上下文,this就不能像预期的那样工作。

关于React

下面是一个React组件(为简洁而缩写)遭遇this问题的例子:

handleClick() {
this.setState(({ clicks }) => ({ // setState is async, use callback to access previous state
clicks: clicks + 1, // increase by 1
}));
}


render() {
return (
<button onClick={this.handleClick}>{this.state.clicks}</button>
);
}

但是为什么,前面的部分与此有什么关系呢?这是因为他们对同一个问题的抽象。如果你看一下React处理事件处理程序:

// Edited to fit answer, React performs other checks internally
// props is the current React component's props, registrationName is the name of the event handle prop, i.e "onClick"
let listener = props[registrationName];
// Later, listener is called

因此,当你执行onClick={this.handleClick}时,方法this.handleClick最终被赋值给变量listener3.。但是现在你看到问题出现了——因为我们已经将this.handleClick赋值给listener,我们不再确切地指定handleClick来自哪里!从React的角度来看,listener只是一个函数,没有附加到任何对象(或者在这种情况下,React组件实例)。我们丢失了上下文,因此解释器无法推断this值来使用this.handleClick0 handleClick

为什么绑定有效

你可能想知道,如果解释器在运行时决定this值,为什么我可以绑定处理程序,使它做的工作?这是因为你可以在运行时使用Function#bind保证 this值。这是通过在函数上设置内部this绑定属性来实现的,允许它不推断this:

this.handleClick = this.handleClick.bind(this);

当执行这一行时,假设在构造函数当前的this被捕获 (React组件实例)中执行,并将其设置为一个全新函数的内部this绑定,该函数从Function#bind返回。这确保了在运行时计算this时,解释器不会尝试推断任何东西,而是使用您提供的this值。

为什么箭头函数属性有效

箭头函数类属性目前通过Babel基于翻译工作:

handleClick = () => { /* Can use this just fine here */ }

就变成:

constructor() {
super();
this.handleClick = () => {}
}

这是由于箭头函数确实绑定了它们自己的this,但它们的封闭范围的this。在本例中,constructorthis指向React组件实例,从而给出正确的this. __abc5


1我使用“method”来指代应该绑定到对象的函数,而“function”则指代那些没有绑定到对象的函数。

2在第二个代码片段中,未定义被记录为3,因为当无法通过特定上下文确定时,this默认为全局执行上下文(非严格模式时为window,否则为undefined)。并且在本例中window.foo不存在,因此产生undefined。

3.如果你深入到事件队列中的事件如何执行的兔子洞中,invokeGuardedCallback将在侦听器上调用。

4实际上是要复杂得多。React内部尝试在监听器上使用Function#apply供自己使用,但这对箭头函数不起作用,因为它们根本不绑定this。这意味着,当箭头函数中的this实际计算时,this将解析模块当前代码的每个执行上下文的每个词法环境。执行上下文最终解析为具有this绑定构造函数,该构造函数具有指向当前React组件实例的this,允许它工作。

您使用的是ES6,因此函数不会自动绑定到“此”上下文。您必须手动将函数绑定到上下文。

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

您的函数需要绑定,以便使用事件处理程序中的状态或道具

在ES5中,只在构造函数中绑定事件处理函数,而不直接在呈现中绑定。如果你直接在渲染中绑定,那么每次你的组件渲染和重新渲染时,它都会创建一个新函数。所以你应该总是在构造函数中绑定它

this.sendContent = this.sendContent.bind(this)

在ES6中,使用箭头函数

当你使用箭头函数时,你不需要做绑定,你也可以远离范围相关的问题

sendContent = (event) => {


}

Alexandre Kirszenberg是对的,但另一件需要注意的重要事情是你把你的装订在哪里。我已经被这种情况困了好几天了(可能是因为我是初学者),但不像其他人,我知道bind(我已经应用了),所以我只是不明白为什么我仍然有这些错误。原来我把绑定的顺序弄错了。

另一个原因可能是我在this中调用了函数。State”,它不知道绑定,因为它恰好在绑定线之上,

以下是我的想法(顺便说一下,这是我第一次发帖,但我认为这很重要,因为我在其他地方找不到解决方案):

constructor(props){
super(props);


productArray=//some array


this.state={
// Create an Array  which will hold components to be displayed
proListing:productArray.map(product=>{return(<ProRow dele={this.this.popRow()} prodName={product.name} prodPrice={product.price}/>)})
}


this.popRow=this.popRow.bind(this);//This was the Issue, This line //should be kept above "this.state"

解决方案:

  1. 在没有显式绑定的情况下,bind与方法名可以使用胖箭头函数语法() = > {}来维护this的上下文。
import React from 'react'


class SomeClass extends React.Component {
constructor(props) {
super(props)
this.state = {
inputContent: 'startValue'
}
}


sendContent = (e) => {
console.log('sending input content ',this.state.inputContent);
}


changeContent = (e) => {
this.setState({inputContent: e.target.value},()=>{
console.log('STATE:',this.state);
})
}


render() {
return (
<div>
<h4>The input form is here:</h4>
Title:
<input type="text" value={this.state.inputContent}
onChange={this.changeContent} />
<button onClick={this.sendContent}>Submit</button>
</div>
)
}
}


export default SomeClass


其他的解决方案:

  1. 在类构造函数中绑定函数。

  2. 在JSX模板转义括号中绑定函数 {this.methodName.bind ()} < / p > < /李>

bind(this)可以解决这个问题,现在如果你不喜欢使用bind,我们可以使用另外两种方法来实现这个目标。

1)与传统方式一样,我们可以在构造函数中使用bind(this),这样当我们将函数用作JSX回调时,this的上下文就是类本身。

class App1 extends React.Component {
constructor(props) {
super(props);
// If we comment out the following line,
// we will get run time error said `this` is undefined.
this.changeColor = this.changeColor.bind(this);
}


changeColor(e) {
e.currentTarget.style.backgroundColor = "#00FF00";
console.log(this.props);
}


render() {
return (
<div>
<button onClick={this.changeColor}> button</button>
</div>
);
}
}

2)如果将函数定义为带有箭头函数的类的属性/字段,则不再需要使用bind(this)

class App2 extends React.Component {
changeColor = e => {
e.currentTarget.style.backgroundColor = "#00FF00";
console.log(this.props);
};
render() {
return (
<div>
<button onClick={this.changeColor}> button 1</button>
</div>
);
}
}

3)如果我们使用箭头函数作为JSX回调,我们也不需要使用bind(this)。更进一步,我们可以传递参数。看起来不错,不是吗?但它的缺点是性能问题,详情请参考ReactJS doco

class App3 extends React.Component {
changeColor(e, colorHex) {
e.currentTarget.style.backgroundColor = colorHex;
console.log(this.props);
}
render() {
return (
<div>
<button onClick={e => this.changeColor(e, "#ff0000")}> button 1</button>
</div>
);
}
}

我已经创建了一个Codepen来演示这些代码片段,希望对您有所帮助。

如果有人能得到这个答案, 下面是一种无需手动绑定所有函数的方法

在构造函数()中:

for (let member of Object.getOwnPropertyNames(Object.getPrototypeOf(this))) {
this[member] = this[member].bind(this)
}

或者在全局变量中创建这个函数。jsx文件

export function bindAllFunctions({ bindTo: dis }) {
for (let member of Object.getOwnPropertyNames(Object.getPrototypeOf(dis))) {
dis[member] = dis[member].bind(dis)
}
}

并在你的构造函数()调用它如下:

bindAllFunctions({ bindTo: this })