无法在事件处理程序中访问 React 实例(this)[重复]
Posted
技术标签:
【中文标题】无法在事件处理程序中访问 React 实例(this)[重复]【英文标题】:Unable to access React instance (this) inside event handler [duplicate] 【发布时间】:2015-06-17 03:34:51 【问题描述】:我正在用 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
【问题讨论】:
这不是问题,但你应该avoid refs。 @FakeRainBrigand 如果没有参考,你将如何解决它? 在您当前的代码中,只需将React.findDOMNode(React.refs.someref).value)
更改为this.state.inputContent
并删除ref="someref"
。
您不需要参考,因为您正在更新状态内部的值。只需发送状态值。
您的函数需要在 ES5 中绑定才能访问函数内部的状态或道具,但是如果您使用箭头函数,则无需手动绑定,绑定会自动进行,您也可以远离范围相关问题
【参考方案1】:
this.changeContent
需要通过this.changeContent.bind(this)
绑定到组件实例,然后才能作为onChange
属性传递,否则函数体中的this
变量不会引用组件实例而是引用@ 987654330@。见Function::bind。
当使用React.createClass
而不是 ES6 类时,组件上定义的每个非生命周期方法都会自动绑定到组件实例。见Autobinding。
请注意,绑定函数会创建一个新函数。你可以直接在 render 中绑定它,这意味着每次渲染组件时都会创建一个新函数,或者在你的构造函数中绑定它,它只会触发一次。
constructor()
this.changeContent = this.changeContent.bind(this);
对
render()
return <input onChange=this.changeContent.bind(this) />;
Refs 是在组件实例上而不是在React.refs
上设置的:您需要将React.refs.someref
更改为this.refs.someref
。您还需要将 sendContent
方法绑定到组件实例,以便 this
引用它。
【讨论】:
在构造函数本身中绑定函数以防止多次创建函数 对不起,我不明白为什么this.changeContent
需要通过this.changeContent.bind(this)
绑定到组件实例。我的意思是,我们通过子类或 React.Component 编写组件,在 ES6 中,类中定义的每个方法都会自动绑定到通过子类/类本身创建的实例。为什么在这里我们需要“手动”完成? React 有什么特别之处吗?还是我对 ES6 类方法的动态感到厌烦?
在 ES6 中,定义在类上的方法不会自动绑定到实例。这就是为什么在需要时需要手动绑定它们的原因。使用 BabelJS,使用 property initializer syntax 和箭头函数,您可以直接定义将自动绑定到实例的方法。 myMethod = () => ...
而不是 myMethod() ...
。
@AlexandreKirszenberg 看看this example:该方法似乎自动绑定到实例...
@marco Here's a different example。当您使用object.method()
调用方法时,method
主体内的this
变量将引用object
。但是如果你将object.method
传递给另一个函数,只会传递函数本身的值,它会丢失object
上下文。这就是为什么在 React 中,您有时需要在将事件处理程序传递给组件之前手动绑定它,以免丢失当前组件实例的上下文。【参考方案2】:
Morhaus 是正确的,但是没有bind
也可以解决这个问题。
您可以将arrow function 与class 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
的结果是一样的。
【讨论】:
这在 TypeScript 中也可以完美运行(通常不必担心在 TypeScript 中的绑定,但我想这是不同的) 这不起作用。我得到“属性声明只能在 .ts 文件中使用” @BHouwens Here it is in the babel REPL。我不知道你在做什么,但你做错了什么。 可能是我设置了一个构造函数,但除此之外,我的示例是相同的并且无法编译。绑定方法虽然有效。 构造函数不会破坏这段代码,你一定有另一个问题。也许您没有正确的插件?这是 2015 预设的一部分,它被称为babel-plugin-transform-class-properties
。如果您向我展示您的代码,我可以告诉您问题所在。 babel repl 为您提供了一个很好的共享链接。【参考方案3】:
当从 React.createClass()
组件定义语法转换到扩展 React.Component
的 ES6 类方式时,这个问题是我们大多数人首先遇到的问题之一。
这是由React.createClass()
与extends React.Component
中的this
上下文差异引起的。
使用React.createClass()
将自动正确绑定this
上下文(值),但使用ES6 类时并非如此。以 ES6 方式(通过扩展 React.Component
)执行此操作时,this
上下文默认为 null
。类的属性不会自动绑定到 React 类(组件)实例。
解决此问题的方法
我一共知道4种通用方法。
在类构造函数中绑定你的函数。许多人认为这是一种最佳实践方法,它完全避免接触 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
的正确实例,即使方法已分离。包使用@autobind
before 方法将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 一样。
Class 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 中提到的帮助库之一。
其他
与你遇到的问题无关,而是你shouldn't overuse refs。
您的第一个倾向可能是在您的应用中使用 refs 来“让事情发生”。如果是这种情况,请花点时间更批判性地思考在组件层次结构中应该拥有状态的位置。
出于类似目的,就像您需要的一样,使用controlled component 是首选方式。我建议您考虑使用您的Component state
。因此,您可以像这样简单地访问该值:this.state.inputContent
。
【讨论】:
这比公认的答案更完整、更有用。 这缺少其他答案中的方法***.com/a/34050078/788260 @Tyrsius ,它就在那里。请参阅我的答案中的方法 #3,一个粗箭头函数 + 类属性提案。 @KaloyanKosev 如果不点击操作而只是简单的方法调用怎么办?【参考方案4】:虽然前面的答案提供了解决方案的基本概述(即绑定、箭头函数、为您执行此操作的装饰器),但我还没有遇到真正解释为什么的答案是必要的——在我看来,这是混乱的根源,并导致不必要的步骤,例如不必要的重新绑定和盲目跟随别人的做法。
this
是动态的
为了了解这个具体情况,简单介绍一下this
是如何工作的。这里的关键是this
是一个运行时绑定,并且依赖于当前的执行上下文。因此为什么它通常被称为“上下文”——提供有关当前执行上下文的信息,以及为什么需要绑定是因为你失去了“上下文”。但是让我用一个 sn-p 来说明这个问题:
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
方法1 的位置——在 foobar
对象上:
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 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基于transpilation工作:
handleClick = () => /* Can use this just fine here */
变成:
constructor()
super();
this.handleClick = () =>
这之所以有效,是因为箭头函数不绑定自己的 this,而是采用其封闭范围的 this
。在本例中,constructor
的 this
指向 React 组件实例,从而为您提供正确的 this
。4
1 我使用“方法”来指代应该绑定到对象的函数,而使用“函数”来指代那些没有绑定的函数。
2 在第二个 sn-p 中,记录 undefined 而不是 3,因为 this
默认为全局执行上下文(window
未处于严格模式时,否则 undefined
)当无法通过特定上下文确定时。在示例中,window.foo
不存在,因此产生未定义。
3 如果您深入了解事件队列中的事件是如何执行的,则在侦听器上调用invokeGuardedCallback
。
4 实际上要复杂得多。 React 在内部尝试在侦听器上使用 Function#apply
以供自己使用,但这不起作用箭头函数,因为它们根本不绑定 this
。这意味着,当实际评估箭头函数内的this
时,this
将解析模块当前代码的每个执行上下文的每个词法环境。最终解析为具有this
绑定的执行上下文是构造函数,它有一个指向当前 React 组件实例的this
,允许它工作。
【讨论】:
【参考方案5】:你可以通过三种方式解决这个问题
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
【讨论】:
【参考方案6】:我们需要将事件函数与构造函数中的组件绑定如下,
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
谢谢
【讨论】:
【参考方案7】:我的建议是使用箭头函数作为属性
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) 并在 props 到达时进行浅层检查
【讨论】:
【参考方案8】:您可以按照以下步骤解决此问题
用
更改 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>
【讨论】:
【参考方案9】:我们必须使用this
bind
我们的函数来获取类中函数的实例。像这样的例子
<button onClick=this.sendContent.bind(this)>Submit</button>
这样this.state
将是有效的对象。
【讨论】:
【参考方案10】:如果有人能得到这个答案, 这是一种无需手动绑定即可绑定所有功能的方法
在构造函数()中:
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 )
【讨论】:
【参考方案11】:出现此问题是因为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>
)
【讨论】:
这也被认为是一种不好的做法,因为每次渲染组件时您都在创建一个新函数。您可以通过const changeContent = () => ...
在您的 ES6 类上定义您的函数,以自动将其绑定到类本身..【参考方案12】:
你好,如果你不想关心绑定你自己的函数调用。您可以使用'class-autobind'并像这样导入它
import autobind from 'class-autobind';
class test extends Component
constructor(props)
super(props);
autobind(this);
在 super 调用之前不要写 autobind,因为它不起作用
【讨论】:
【参考方案13】:如果您想在构造函数语法中保留绑定,您可以使用proposal-bind-operator 并转换您的代码,如下所示:
constructor()
this.changeContent = ::this.changeContent;
而不是:
constructor()
this.changeContent = this.changeContent.bind(this);
更简单,不需要bind(this)
或fatArrow
。
【讨论】:
【参考方案14】:这个问题发生在 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 改变而重新渲染,否则可能会产生性能问题。
【讨论】:
【参考方案15】:您使用的是 ES6,因此函数不会自动绑定到“this”上下文。您必须手动将函数绑定到上下文。
constructor(props)
super(props);
this.changeContent = this.changeContent.bind(this);
【讨论】:
【参考方案16】:您的函数需要绑定才能在事件处理程序中使用状态或道具
在 ES5 中,仅在构造函数中绑定事件处理函数,而不是直接在渲染中绑定。如果您直接在渲染中进行绑定,那么每次您的组件渲染和重新渲染时它都会创建一个新函数。所以你应该总是在构造函数中绑定它
this.sendContent = this.sendContent.bind(this)
在 ES6 中,使用箭头函数
当你使用箭头函数时,你不需要做绑定,你也可以远离范围相关的问题
sendContent = (event) =>
【讨论】:
【参考方案17】: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"
【讨论】:
【参考方案18】:解决方案:
-
没有显式绑定,
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
其他解决方案:
在类构造函数中绑定你的函数。
在 JSX 模板转义大括号中绑定您的函数 this.methodName.bind(this)
【讨论】:
【参考方案19】: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 来演示这些代码sn-ps,希望对您有所帮助。
【讨论】:
以上是关于无法在事件处理程序中访问 React 实例(this)[重复]的主要内容,如果未能解决你的问题,请参考以下文章
如何在服务器端 React 中访问 JSX 组件的 DOM 事件处理程序