首页 文章

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

提问于
浏览
173

我在ES6中编写了一个简单的组件(使用BabelJS),函数 this.setState 无效 .

典型错误包括类似的内容

无法读取undefined的属性'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

16 回答

  • 0

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

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

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

    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 方法绑定到组件实例,以便 this 引用它 .

  • 0

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

    您可以将arrow functionclass properties proposal一起使用:

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

    因为箭头函数是在构造函数的范围内声明的,并且因为箭头函数从它们的声明范围维护 this ,所以它都可以工作 . 这里的缺点是这些不是原型上的函数,它们都将用每个组件重新创建 . 然而,这并不是一个缺点,因为 bind 导致同样的事情 .

  • 86

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

    It is caused by the this context differences in React.createClass() vs extends React.Component .

    使用 React.createClass() 将自动正确绑定 this context(值),但使用ES6类时则不然 . 在执行ES6方式时(通过扩展 React.Component ), this 上下文默认为 null . 该类的属性不会自动绑定到React类(组件)实例 .


    解决此问题的方法

    我知道共有4种一般方法 .

    • Bind your functions in the class constructor . 被许多人认为是一种最佳实践方法,它完全避免触及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>
        );
      }
    }
    
    • Bind your functions inline . 你仍然可以在一些教程/文章/等中找到这里和那里使用的这种方法,所以's important you'了解它 . 它与#1的概念相同,但请注意,绑定函数会为每次重新渲染创建一个新函数 .
    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 . 在箭头函数之前,每个新函数都定义了自己的 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>
        );
      }
    }
    
    • Use utility function library to automatically bind your functions . 有一些实用程序库可以自动完成这项工作 . 以下是一些流行的,仅举几例:

    • Autobind Decorator是一个NPM包,它将类的方法绑定到正确的 this 实例,即使这些方法是分离的 . 包 uses @autobind before methods to bind this to the correct reference 到组件的上下文 .

    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一样 .

    • Class Autobind是另一个广泛用于解决此绑定问题的NPM包 . 与Autobind Decorator不同,它不使用装饰器模式,但实际上是 just uses a function inside your constructor that automatically binds 组件的方法来正确引用 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中提到的一个帮助库 .


    其他

    这与你遇到的问题无关,但是你shouldn't overuse refs .

    您的第一个倾向可能是在您的应用中使用refs“让事情发生” . 如果是这种情况,请花一点时间,更关键地考虑组件层次结构中应该拥有哪个状态 .

    出于类似的目的,就像你需要的那样,使用controlled component是首选方式 . 我建议你考虑使用你的Component state . 因此,您可以像这样访问值: this.state.inputContent .

  • 0

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

    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
    

    谢谢

  • 0

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

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

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

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

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

    不要在超级调用之前编写autobind,因为它不起作用

  • 0

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

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

    代替 :

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

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

  • 0

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

    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
    
  • 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) 并在道具到达时进行浅层检查

  • 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不会因为新功能道具的改变而受到影响,或者可能会产生性能问题 .

  • 43

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

    使用更改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>
    
  • 0

    虽然之前的答案已经提供了解决方案的基本概述(即绑定,箭头功能,为你做这个的装饰器),但我还没有找到一个答案,这实际上解释了 why 这是必要的 - 在我看来这是根混乱,并导致不必要的步骤,如不必要的重新绑定和盲目跟随别人的行为 .

    这是动态的

    要了解这种具体情况,请简要介绍 this 的工作原理 . 这里的关键是 this 是一个运行时绑定,它取决于当前的执行上下文 . 因此,为什么它通常被称为当前执行上下文的"context" -giving信息,以及为什么需要绑定是因为你松散"context" . 但是,让我用一个片段来说明问题:

    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 去了哪里?答案在"context",或者你如何执行一个函数 . 比较我们如何调用这些函数:

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

    注意区别 . 在第一个示例中,我们在 foobar 对象上准确指定了 bar method1所在的位置:

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

    但是在第二个中,我们将方法存储到一个新变量中,并使用该变量来调用该方法,而无需明确说明该方法实际存在的位置, thus losing context

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

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

    与React有关

    以下是一个由于 this 问题导致的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 handles event handlers

    // 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 最终会分配给变量 listener 3.但现在您看到问题出现了 - 因为我们已将 this.handleClick 分配给 listener ,我们不再确切地指定 handleClick 的来源!从React的角度来看, listener 只是一些函数,没有附加到任何对象(或者在这种情况下,React组件实例) . 我们丢失了上下文,因此解释器无法推断 this 值以使用 inside 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 . 在这种情况下, constructorthis ,指向React组件实例 - 从而为您提供正确的 this .4


    1我使用"method"来引用一个应该绑定到一个对象的函数,而"function"则用于那些未绑定的函数 .

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

    3如果您查看事件队列中事件的执行情况,则会在侦听器上调用invokeGuardedCallback .

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

  • 0

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

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

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

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

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

    在ES6中,使用箭头功能

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

    sendContent = (event) => {
    
    }
    
  • 0

    我们必须 bind 我们的函数 this 来获取类中函数的实例 . 就像在例子中一样

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

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

  • 206

    亚历山大·基尔森伯格(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"
    

相关问题