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

典型的错误包括

无法读取undefined的属性“setState”

or

这一点。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

当前回答

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"

其他回答

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

这是动态的

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

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 method1在foobar对象上的位置:

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

但在第二种方法中,我们将方法存储到一个新变量中,并使用该变量来调用该方法,而不显式地声明该方法实际存在的位置,从而丢失上下文:

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

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

关于React

下面是一个React组件(为简洁起见而缩写)遇到这个问题的例子:

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。但现在你看到问题了,因为我们分配了这个。handleClick到监听器,我们不再指定handleClick来自哪里!从React的角度来看,监听器只是一个函数,没有附加到任何对象(或者在这种情况下,React组件实例)。我们丢失了上下文,因此解释器无法推断出在handleClick内部使用的this值。

为什么绑定有效

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

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

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

为什么箭头函数属性有效

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

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

就变成:

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

这是因为箭头函数不绑定自己的this,而是取其封闭范围的this。在本例中,构造函数是this,它指向React组件实例——因此给出了正确的this.4


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

2在第二个代码片段中,未定义被记录为日志,而不是3,因为当不能通过特定上下文确定时,默认为全局执行上下文(非严格模式时为窗口,否则为未定义)。在示例窗口中。Foo不存在,因此产生undefined。

如果你深入到事件队列中事件是如何执行的,监听器上调用invokeGuardedCallback。

4 It's actually a lot more complicated. React internally tries to use Function#apply on listeners for its own use, but this does not work arrow functions as they simply do not bind this. That means, when this inside the arrow function is actually evaluated, the this is resolved up each lexical environment of each execution context of the current code of the module. The execution context which finally resolves to have a this binding is the constructor, which has a this pointing to the current React component instance, allowing it to work.

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

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

而不是:

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

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

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

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

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

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

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

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

vs

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

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

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

这是由React.createClass()与extends React.Component中的上下文差异引起的。

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


解决该问题的方法

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

Bind your functions in the class constructor. Considered by many as a best-practice approach that avoids touching JSX at all and doesn't create a new function on each component re-render. 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> ); } } 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> ); } } 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> ); } } or class SomeClass extends React.Component { handleClick = () => { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick}></button> ); } } 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 is smart enough to let us bind all methods inside a component class at once, just like approach #1. Class Autobind is another NPM package that is widely used to solve this binding issue. Unlike Autobind Decorator, it does not use of the decorator pattern, but really just uses a function inside your constructor that automatically binds the Component's methods to the correct reference of 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: Other very similar library is React Autobind.


建议

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


其他

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

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

对于类似的目的,就像您需要的一样,使用受控组件是首选的方法。我建议您考虑使用Component状态。所以,你可以像这样访问这个值:this.state. inputcontent。