240

我正在用 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
4

19 回答 19

270

this.changeContentthis.changeContent.bind(this)在作为onChangeprop传递之前需要绑定到组件实例 via ,否则this函数体中的变量将不会引用组件实例而是引用window. 见函数::绑定

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

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

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

对比

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

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

于 2015-04-11T13:09:14.063 回答
105

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结果相同。

于 2015-12-02T18:15:59.160 回答
57

当从React.createClass()组件定义语法过渡到 ES6 类扩展React.Component.

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

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


解决此问题的方法

我总共知道 4 种通用方法。

  1. 在类构造函数中绑定你的函数。许多人认为这是一种最佳实践方法,它完全避免接触 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. 内联绑定你的函数。您仍然可以在一些教程/文章/等中到处找到这种方法,因此了解它很重要。它与 #1 的概念相同,但请注意,绑定函数会在每次重新渲染时创建一个新函数。

    class SomeClass extends React.Component {
      handleClick() {
        console.log(this); // the React Component instance
      }
      render() {
        return (
          <button onClick={this.handleClick.bind(this)}></button>
        );
      }
    }
    
  3. 使用粗箭头函数。在箭头函数之前,每个新函数都定义了自己的this值。但是,箭头函数不会创建自己的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. 使用实用函数库自动绑定您的函数。有一些实用程序库可以自动为您完成这项工作。以下是一些流行的,仅举几例:

    • Autobind Decorator是一个 NPM 包,它将类的方法绑定到 的正确实例this,即使方法已分离。包使用@autobindbefore 方法绑定this到对组件上下文的正确引用。

      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:其他非常相似的库是React Autobind


推荐

如果我是你,我会坚持方法#1。但是,一旦您在类构造函数中获得大量绑定,我建议您探索方法 #4 中提到的帮助程序库之一。


其他

它与您遇到的问题无关,但您不应过度使用 refs

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

出于类似目的,就像您需要的一样,使用受控组件是首选方式。我建议您考虑使用您的Componentstate。因此,您可以像这样简单地访问值:this.state.inputContent.

于 2016-12-21T22:11:58.527 回答
9

尽管前面的答案提供了解决方案的基本概述(即绑定、箭头函数、为您执行此操作的装饰器),但我还没有找到一个答案实际上解释了为什么这是必要的——我认为这是根本混乱,并导致不必要的步骤,例如不必要的重新绑定和盲目跟随别人的做法。

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方法1的位置——在foobar对象上:

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

但是在第二种情况下,我们将方法存储到一个新变量中,并使用该变量调用该方法,而没有明确说明该方法实际存在的位置,从而丢失了上下文

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

这就是问题所在,当您将方法存储在变量中时,有关该方法所在位置(执行该方法的上下文)的原始信息会丢失。没有这些信息,在运行时,JavaScript 解释器无法绑定正确的this- 没有特定的上下文,this无法按预期工作2

与反应有关

这是一个遇到问题的 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.handleClicklistener,我们不再指定确切handleClick的来自哪里!从 React 的角度来看,listener它只是一些函数,没有附加到任何对象(或者在这种情况下,是 React 组件实例)。我们失去了上下文,因此解释器无法推断要在 insidethis中使用的值。 handleClick

为什么绑定有效

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

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

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

为什么箭头函数属性有效

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

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

变成:

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

这之所以有效,是因为箭头函数绑定它们自己的 this,而是采用this它们的封闭范围。在本例中,constructor'sthis指向 React 组件实例——从而为您提供正确的this. 4


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

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

3如果您深入了解事件队列中的事件是如何执行的,invokeGuardedCallback则在侦听器上调用。

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

于 2018-08-09T06:01:26.003 回答
2

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

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
于 2017-09-13T09:43:26.537 回答
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

谢谢

于 2017-08-21T09:27:59.383 回答
1

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

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

因为第二种方法会在每次渲染调用时生成新函数实际上这意味着新的指针新版本的道具,而不是如果你以后关心性能你可以使用React.PureComponent或在React.Component中 你可以覆盖shouldComponentUpdate(nextProps, nextState)并在道具到达时进行浅检查

于 2017-12-14T13:25:50.930 回答
1

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

更改 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>
于 2018-02-06T07:24:10.287 回答
1

我们必须使用bind我们的函数this来获取类中函数的实例。像这样的例子

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

这种方式this.state将是有效的对象。

于 2018-07-09T07:05:16.573 回答
1

如果有人会得到这个答案,这是一种绑定所有功能而无需手动绑定它们的方法

在构造函数()中:

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

或在 global.jsx 文件中创建此函数

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

并在您的 constructor() 中调用它:

bindAllFunctions({ bindTo: this })
于 2019-11-23T00:48:08.183 回答
0

发生此问题的原因是this.changeContent并且onClick={this.sendContent}不受组件实例的this的约束。

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

render() {
    return (

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

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

    )
  }
于 2016-06-26T05:51:01.450 回答
0

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

import autobind from 'class-autobind';

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

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

于 2017-04-13T09:36:13.307 回答
0

如果您想在构造函数语法中保留绑定,您可以使用proposal-bind-operator并转换您的代码,如下所示:

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

代替 :

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

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

于 2017-07-06T09:00:05.070 回答
0

这个问题发生在 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 不会因为新的函数 props 改变而重新渲染,否则可能会产生性能问题。

于 2018-01-10T13:30:59.967 回答
0

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

constructor(props) {
  super(props);
  this.changeContent = this.changeContent.bind(this);
}
于 2018-08-14T04:57:13.150 回答
0

您的函数需要绑定才能在事件处理程序中使用状态或道具

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

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

在 ES6 中,使用箭头函数

当您使用箭头函数时,您无需进行绑定,您也可以远离范围相关的问题

sendContent = (event) => {

}
于 2018-08-18T01:28:44.057 回答
0

Alexandre Kirszenberg 是正确的,但是要注意的另一件重要的事情是,您将装订放在哪里。我已经被困了好几天了(可能是因为我是初学者),但与其他人不同,我知道绑定(我已经申请过)所以我无法理解为什么我仍然拥有那些错误。事实证明,我的绑定顺序错误。

另一个可能是我在“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"
于 2018-09-22T12:38:54.910 回答
0

解决方案:

  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(this)}

于 2019-05-03T22:25:01.303 回答
0

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来演示这些代码片段,希望对您有所帮助。

于 2019-06-28T01:22:13.560 回答