Javascript 无法在事件处理程序中访问 React 实例 (this)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29577977/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Unable to access React instance (this) inside event handler
提问by user3696212
I am writing a simple component in ES6 (with BabelJS), and functions this.setStateis not working.
我正在用 ES6(使用 BabelJS)编写一个简单的组件,但函数this.setState不起作用。
Typical errors include something like
典型的错误包括
Cannot read property 'setState' of undefined
无法读取未定义的属性“setState”
or
或者
this.setState is not a function
this.setState 不是函数
Do you know why? Here is the code:
你知道为什么吗?这是代码:
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
回答by Alexandre Kirszenberg
this.changeContentneeds to be bound to the component instance via this.changeContent.bind(this)before being passed as the onChangeprop, otherwise the thisvariable in the body of the function will not refer to the component instance but to window. See Function::bind.
this.changeContentthis.changeContent.bind(this)在作为onChangeprop传递之前需要绑定到组件实例 via否则this函数体中的变量将不会引用组件实例而是指向window. 请参阅函数::绑定。
When using React.createClassinstead of ES6 classes, every non-lifecycle method defined on a component is automatically bound to the component instance. See Autobinding.
当使用React.createClass而不是 ES6 类时,在组件上定义的每个非生命周期方法都会自动绑定到组件实例。请参阅自动绑定。
Be aware that binding a function creates a new function. You can either bind it directly in render, which means a new function will be created every time the component renders, or bind it in your constructor, which will only fire once.
请注意,绑定一个函数会创建一个新函数。你可以直接在渲染中绑定它,这意味着每次组件渲染时都会创建一个新函数,或者在你的构造函数中绑定它,它只会触发一次。
constructor() {
this.changeContent = this.changeContent.bind(this);
}
vs
对比
render() {
return <input onChange={this.changeContent.bind(this)} />;
}
Refs are set on the component instance and not on React.refs: you need to change React.refs.somerefto this.refs.someref. You'll also need to bind the sendContentmethod to the component instance so that thisrefers to it.
Refs 是在组件实例上设置的,而不是在React.refs: 您需要更改React.refs.someref为this.refs.someref. 您还需要将该sendContent方法绑定到组件实例,以便this引用它。
回答by Kyeotic
Morhaus is correct, but this can be solved without bind.
Morhaus 是正确的,但是没有bind.
You can use an arrow functiontogether with the class propertiesproposal:
class SomeClass extends React.Component {
changeContent = (e) => {
this.setState({inputContent: e.target.value})
}
render() {
return <input type="text" onChange={this.changeContent} />;
}
}
Because the arrow function is declared in the scope of the constructor, and because arrow functions maintain thisfrom their declaring scope, it all works. The downside here is that these wont be functions on the prototype, they will all be recreated with each component. However, this isn't much of a downside since bindresults in the same thing.
因为箭头函数是在构造函数的作用this域中声明的,并且因为箭头函数从它们的声明作用域维护,所以一切都有效。这里的缺点是这些不会是原型上的函数,它们都将与每个组件一起重新创建。然而,这并不是什么坏处,因为bind结果是一样的。
回答by Kaloyan Kosev
This issue is one of the first things most of us experience, when transitioning from the React.createClass()component definition syntax to the ES6 class way of extending React.Component.
这个问题是我们大多数人在从React.createClass()组件定义语法转换到 ES6 类扩展React.Component.
It is caused by the thiscontext differences in React.createClass()vs extends React.Component.
这是由vs 中的this上下文差异引起的React.createClass()extends React.Component。
Using React.createClass()will automatically bind thiscontext (values) correctly, but that is not the case when using ES6 classes. When doing it the ES6 way (by extending React.Component) the thiscontext is nullby default. Properties of the class do not automatically bind to the React class (component) instance.
UsingReact.createClass()将自动this正确绑定上下文(值),但使用 ES6 类时情况并非如此。当使用 ES6 方式(通过扩展React.Component)时,this上下文是null默认的。类的属性不会自动绑定到 React 类(组件)实例。
Approaches to Solve this Issue
解决此问题的方法
I know a total of 4 general approaches.
我总共知道 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
thisvalue. However, the arrow function does not create its ownthiscontext, sothishas 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 Decoratoris an NPM package which binds methods of a class to the correct instance of
this, even when the methods are detached. The package uses@autobindbefore methods to bindthisto the correct referenceto 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 Autobindis 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 bindsthe 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.
在类构造函数中绑定您的函数。许多人认为这是一种最佳实践方法,它完全避免接触 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> ); } }内联绑定您的函数。您仍然可以在一些教程/文章/等中找到这里和那里使用的这种方法,因此了解它很重要。它与#1 的概念相同,但请注意,每次重新渲染时绑定一个函数都会创建一个新函数。
class SomeClass extends React.Component { handleClick() { console.log(this); // the React Component instance } render() { return ( <button onClick={this.handleClick.bind(this)}></button> ); } }使用粗箭头函数。在箭头函数之前,每个新函数都定义了自己的
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> ); } }使用实用函数库自动绑定您的函数。有一些实用程序库可以自动为您完成这项工作。以下是一些流行的,仅举几例:
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。
Recommendation
推荐
If I were you, I would stick with approach #1. However, as soon as you get a ton of binds in your class constructor, I would recommend you to explore one of the helper libraries mentioned in approach #4.
如果我是你,我会坚持方法#1。但是,一旦您在类构造函数中获得大量绑定,我建议您探索方法 #4 中提到的帮助程序库之一。
Other
其他
It's not related to the issue you have, but you shouldn't overuse refs.
这与您遇到的问题无关,但您不应过度使用 refs。
Your first inclination may be to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy.
您的第一个倾向可能是使用 refs 在您的应用程序中“使事情发生”。如果是这种情况,请花点时间更批判性地思考状态应该在组件层次结构中的哪个位置拥有。
For similar purposes, just like the one you need, using a controlled componentis the preferred way. I suggest you to consider using your Component state. So, you can simply access the value like this: this.state.inputContent.
出于类似目的,就像您需要的那样,使用受控组件是首选方式。我建议您考虑使用您的Componentstate。所以,你可以简单地访问这样的值:this.state.inputContent。
回答by Andrew Li
Although the previous answers have provided the basic overview of solutions (i.e. binding, arrow functions, decorators that do this for you), I've yet to come across an answer which actually explains whythis is necessary—which in my opinion is the root of confusion, and leads to unnecessary steps such as needless rebinding and blindly following what others do.
尽管之前的答案已经提供了解决方案的基本概述(即绑定、箭头函数、为您执行此操作的装饰器),但我还没有找到真正解释为什么这是必要的答案——在我看来这是根本混乱,并导致不必要的步骤,例如不必要的重新绑定和盲目跟随他人的做法。
thisis dynamic
this是动态的
To understand this specific situation, a brief introduction to how thisworks. The key thing here is that thisis a runtime binding and depends on the current execution context. Hence why it's commonly referred to as "context"—giving information on the current execution context, and why you need to bind is because you loose "context". But let me illustrate the issue with a snippet:
要了解这种特定情况,请简要介绍其this工作原理。这里的关键是这this是一个运行时绑定,取决于当前的执行上下文。因此,为什么它通常被称为“上下文”——提供有关当前执行上下文的信息,以及为什么需要绑定是因为你失去了“上下文”。但是让我用一个片段来说明这个问题:
const foobar = {
bar: function () {
return this.foo;
},
foo: 3,
};
console.log(foobar.bar()); // 3, all is good!
In this example, we get 3, as expected. But take this example:
在这个例子中,我们得到了3,正如预期的那样。但以这个例子为例:
const barFunc = foobar.bar;
console.log(barFunc()); // Uh oh, undefined!
It may be unexpected to find that it logs undefined—where did the 3go? The answer lies in "context", or how you executea function. Compare how we call the functions:
可能会意外地发现它记录了 undefined ——3去哪儿了?答案在于“上下文”,或者你如何执行一个函数。比较我们如何调用函数:
// Example 1
foobar.bar();
// Example 2
const barFunc = foobar.bar;
barFunc();
Notice the difference. In the first example, we are specifying exactly where the barmethod1is located—on the foobarobject:
注意区别。在第一个示例中,我们准确指定了bar方法1所在的位置——在foobar对象上:
foobar.bar();
^^^^^^
But in the second, we store the method into a new variable, and use that variable to call the method, without explicitly stating where the method actually exists, thus losing context:
但是在第二个中,我们将方法存储到一个新变量中,并使用该变量调用该方法,而没有明确说明该方法实际存在的位置,从而丢失了上下文:
barFunc(); // Which object is this function coming from?
And therein lies the problem, when you store a method in a variable, the original information about where that method is located (the context in which the method is being executed), is lost. Without this information, at runtime, there is no way for the JavaScript interpreter to bind the correct this—without specific context, thisdoes not work as expected2.
这就是问题所在,当您将方法存储在变量中时,有关该方法所在位置(执行该方法的上下文)的原始信息丢失了。如果没有这些信息,在运行时,JavaScript 解释器就无法绑定正确的this- 没有特定的上下文,this无法按预期工作2。
Relating to React
与反应有关
Here's an example of a React component (shortened for brevity) suffering from the thisproblem:
这是一个遇到问题的 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>
);
}
But why, and how does the previous section relate to this? This is because they suffer from an abstraction of the same problem. If you take a look how React handles event handlers:
但是为什么,以及上一节与此有何关系?这是因为他们遭受了同一问题的抽象。如果你看看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
So, when you do onClick={this.handleClick}, the method this.handleClickis eventually assigned to the variable listener3. But now you see the problem arise—since we've assigned this.handleClickto listener, we no longer specify exactly where handleClickis coming from! From React's point of view, listeneris just some function, not attached to any object (or in this case, React component instance). We have lost context and thus the interpreter cannot infer a thisvalue to use insidehandleClick.
因此,当您这样做时onClick={this.handleClick},该方法this.handleClick最终会分配给变量listener3。但是现在你看到问题出现了——因为我们已经赋值this.handleClick给listener,我们不再确切地指定handleClick来自哪里!从 React 的角度来看,listener只是一些函数,不附加到任何对象(或者在这种情况下,React 组件实例)。我们丢失了上下文,因此解释器无法推断出this要在 inside中使用的值handleClick。
Why binding works
为什么绑定有效
You might be wondering, if the interpreter decides the thisvalue at runtime, why can I bind the handler so that it does work? This is because you can use Function#bindto guaranteethe thisvalue at runtime. This is done by setting an internal thisbinding property on a function, allowing it to not infer this:
您可能想知道,如果解释器this在运行时决定该值,为什么我可以绑定处理程序使其工作?这是因为您可以使用Function#bind来保证this运行时的值。这是通过this在函数上设置内部绑定属性来完成的,允许它不推断this:
this.handleClick = this.handleClick.bind(this);
When this line is executed, presumably in the constructor, the current thisis captured(the React component instance) and set as an internal thisbinding of a entirely new function, returned from Function#bind. This makes sure that when thisis being calculated at runtime, the interpreter will not try to infer anything, but use the provided thisvalue you given it.
当这一行被执行时,大概是在构造函数中,当前this被捕获(React 组件实例)并设置为一个this全新函数的内部绑定,从Function#bind. 这确保this在运行时计算时,解释器不会尝试推断任何内容,而是使用this您提供的值。
Why arrow function properties work
为什么箭头函数属性有效
Arrow function class properties currently work through Babel based on the transpilation:
箭头函数类属性目前通过基于转译的 Babel 工作:
handleClick = () => { /* Can use this just fine here */ }
Becomes:
变成:
constructor() {
super();
this.handleClick = () => {}
}
And this works due to the fact arrow functions do notbind their own this, but take the thisof their enclosing scope. In this case, the constructor's this, which points to the React component instance—thus giving you the correct this.4
这是因为箭头函数不绑定它们自己的 this ,而是占用this它们封闭范围的the 。在这种情况下,指向 React 组件实例的constructor's this,从而为您提供正确的this. 4
1I use "method" to refer to a function that is supposed to be bound to an object, and "function" for those not.
1我使用“方法”来指代应该绑定到对象的函数,而使用“函数”来指代没有绑定到对象的函数。
2In the second snippet, undefined is logged instead of 3 because thisdefaults to the global execution context (windowwhen not in strict mode, or else undefined) when it cannot be determined via specific context. And in the example window.foodoes not exist thus yielding undefined.
2在第二个代码段中,记录 undefined 而不是 3,因为当无法通过特定上下文确定时,this默认为全局执行上下文(window当不是严格模式时,否则为 else undefined)。并且在示例window.foo中不存在因此产生未定义。
3If you go down the rabbit hole of how events in the event queue are executed, invokeGuardedCallbackis called on the listener.
3如果您深入了解事件队列中的事件是如何执行的,invokeGuardedCallback则在侦听器上调用。
4It's actually a lot more complicated. React internally tries to use Function#applyon listeners for its own use, but this does not work arrow functions as they simply do not bind this. That means, when thisinside the arrow function is actually evaluated, the thisis 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 thisbinding isthe constructor, which has a thispointing to the current React component instance, allowing it to work.
4它实际上要复杂得多。React 内部尝试将Function#apply侦听器用于自己的用途,但这不起作用箭头函数,因为它们根本不绑定this。这意味着,当this实际评估箭头函数时,this会解析模块当前代码的每个执行上下文的每个词法环境。最终解析为具有this绑定的执行上下文是构造函数,它有一个this指向当前 React 组件实例的指针,允许它工作。
回答by Liju Kuriakose
You can tackle this by three ways
你可以通过三种方式解决这个问题
1.Bind the event function in the constructor itself as follows
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.Bind when it is called
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.By using Arrow functions
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
回答by Ivan Mjartan
My recommendation is use arrow functions as a properties
我的建议是使用箭头函数作为属性
class SomeClass extends React.Component {
handleClick = () => {
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={this.handleClick}></button>
);
}
}
and do not use arrow functions as
并且不要使用箭头函数作为
class SomeClass extends React.Component {
handleClick(){
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={()=>{this.handleClick}}></button>
);
}
}
because second approach will generate new function every render call in fact this means new pointer new version of props, than if you will later care about performance you are able use React.PureComponentor in React.Componentyou can override shouldComponentUpdate(nextProps, nextState)and shallow check when props arrived
因为第二种方法会在每次渲染调用时生成新函数,实际上这意味着新的指针新版本的 props,如果你以后会关心性能,你可以使用React.PureComponent或者在React.Component 中你可以覆盖shouldComponentUpdate(nextProps, nextState)并在道具到达时进行浅层检查
回答by Liju Kuriakose
We need to bind the event function with the component in constructor as follows,
我们需要将事件函数与构造函数中的组件绑定如下,
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
Thanks
谢谢
回答by azmul hossain
You can solve this following these steps
您可以按照以下步骤解决此问题
Change sendContent function with
更改 sendContent 函数
sendContent(e) {
console.log('sending input content '+this.refs.someref.value)
}
Change render function with
更改渲染功能
<input type="text" ref="someref" value={this.state.inputContent}
onChange={(event)=>this.changeContent(event)} />
<button onClick={(event)=>this.sendContent(event)}>Submit</button>
回答by Mustkeem K
We have to bindour function with thisto get instance of the function in class. Like so in example
我们必须使用bind我们的函数this来获取类中函数的实例。像这样的例子
<button onClick={this.sendContent.bind(this)}>Submit</button>
This way this.statewill be valid object.
这种方式this.state将是有效的对象。
回答by coder
if anyone will ever reach this answer, here is a way to bind all of the functions without needing to bind them manually
如果有人会得到这个答案,这里有一种方法可以绑定所有函数而无需手动绑定它们
in the constructor():
在构造函数()中:
for (let member of Object.getOwnPropertyNames(Object.getPrototypeOf(this))) {
this[member] = this[member].bind(this)
}
or create this function in a global.jsx file
或在 global.jsx 文件中创建此函数
export function bindAllFunctions({ bindTo: dis }) {
for (let member of Object.getOwnPropertyNames(Object.getPrototypeOf(dis))) {
dis[member] = dis[member].bind(dis)
}
}
and in your constructor() call it like:
并在您的构造函数()中调用它:
bindAllFunctions({ bindTo: this })

