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

2020/10/14 15:01 · javascript ·  · 0评论

我正在用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.changeContentthis.changeContent.bind(this)在作为onChangeprop传递之前,需要先通过绑定到组件实例,否则this函数主体中的变量将不会引用组件实例,而会引用window参见Function :: bind

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

请注意,绑定功能会创建一个新功能。您可以将其直接绑定到render中,这意味着每次渲染该组件时都会创建一个新函数,或者将其绑定到构造函数中,后者只会触发一次。

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

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

引用是在组件实例上设置的,而不是在实例上设置的React.refs:您需要更改React.refs.somerefthis.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} />;
  }
}

因为arrow函数是在构造函数的范围内声明的,并且因为arrow函数this是在声明范围内维护的,所以它都可以工作。这里的缺点是这些将不是原型上的函数,它们将与每个组件一起重新创建。但是,这不会带来太大的负面影响,因为会bind导致相同的结果。

React.createClass()组件定义语法过渡到扩展的ES6类方法时,此问题是我们大多数人首先遇到的事情之一React.Component

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

使用React.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值。但是,arrow函数不会创建自己的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不同,它不使用装饰器模式,而实际上只是在构造函数中使用一个函数该函数自动将Component的方法绑定到的正确引用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


建议

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


其他

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

您的第一个倾向可能是使用引用在应用程序中“使事情成真”。如果是这种情况,请花点时间仔细考虑一下在组件层次结构中应在何处拥有状态。

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

尽管前面的答案已经提供了解决方案的基本概述(即绑定,箭头功能,为您完成此操作的装饰器),但我还没有遇到一个答案,该答案实际上解释了为什么这样做是必要的-我认为这是根本造成混乱,并导致不必要的步骤,例如不必要的重新绑定和盲目跟随他人的行为。

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去哪里了?答案在于“ context”或如何执行功能。比较我们如何调用函数:

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

注意差异。在第一个示例中,我们精确地指定了bar方法1foobar对象上的位置

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

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

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如何处理事件处理程序

// 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组件实例)。我们已经失去了上下文,因此解释器无法推断在内部this使用 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,这点在阵营组件实例,从而给你正确的this4


1我使用“方法”来指代应该绑定到对象的功能,而使用“方法”来指代那些未绑定的功能。

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

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

4实际上要复杂得多React在内部尝试将其用于Function#apply侦听器以供其自己使用,但这不适用于箭头函数,因为它们只是不绑定this这意味着,当this实际评估arrow函数内部,将this在模块当前代码的每个执行上下文的每个词法环境中进行解析。最终解析为具有this绑定的执行上下文构造函数,该构造函数具有this指向当前React组件实例指针,从而使其能够工作。

您可以通过三种方式解决此问题

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

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

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

谢谢

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

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指针,而不是如果您以后在意性能,则可以使用React.PureComponent或在React.Component中, 您可以覆盖shouldComponentUpdate(nextProps,nextState)并在道具到达时进行浅层检查

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

更改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>

我们必须对bind函数进行操作,this以获取类中函数的实例。像这样

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

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

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

在constructor()中:

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

发生此问题的原因是this.changeContentonClick={this.sendContent}并且不受限于组件实例的实例。

There is another solution (In addition to use bind() in the constructor() ) to use the arrow functions of ES6 which share the same lexical scope of the surrounding code and maintain this , so you can change your code in render() to be :

render() {
    return (

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

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

    )
  }

Hello if you want to dont care about binding yourself your function call. You can use 'class-autobind' and import it like that

import autobind from 'class-autobind';

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

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

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

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

代替 :

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

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

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


there are several methods to solve the problem. but you need to know which method is best and why? In general, we recommend that binding your functions in the class constructor or use a arrow function.

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

these two methods will not creates a new function when the component render everytime. so our ChildComponent will not reRender becaue of the new function props change, or may produce the performance problem.

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

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

您的函数需要绑定才能与事件处理程序中的状态或道具一起玩

在ES5中,仅将事件处理函数绑定到构造函数中,而不直接绑定到render中。如果您直接在render中绑定,则每次您的组件渲染并重新渲染时,它都会创建一个新函数。因此,您应该始终将其绑定到构造函数中

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

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

本文地址:http://javascript.askforanswer.com/wufazaishijianchulichengxuzhongfangwenreactshilici.html
文章标签: ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!