无法访问事件处理程序中的 React 实例 (this)
Unable to access React instance (this) inside event handler
我正在用 ES6(使用 BabelJS)编写一个简单的组件,但函数 this.setState
无法正常工作。
典型的错误包括
Cannot read property 'setState' of undefined
或
this.setState is not a function
你知道为什么吗?这是代码:
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.changeContent
在作为 onChange
prop 传递之前需要通过 this.changeContent.bind(this)
绑定到组件实例,否则函数体中的 this
变量不会引用组件实例,而是引用 window
。参见 Function::bind。
当使用 React.createClass
而不是 ES6 类 时,组件上定义的每个非生命周期方法都会自动绑定到组件实例。参见 Autobinding。
请注意,绑定一个函数会创建一个新函数。您可以直接在渲染中绑定它,这意味着每次组件渲染时都会创建一个新函数,或者在您的构造函数中绑定它,它只会触发一次。
constructor() {
this.changeContent = this.changeContent.bind(this);
}
对
render() {
return <input onChange={this.changeContent.bind(this)} />;
}
引用是在组件实例上设置的,而不是在 React.refs
上设置的:您需要将 React.refs.someref
更改为 this.refs.someref
。您还需要将 sendContent
方法绑定到组件实例,以便 this
引用它。
Morhaus 是正确的,但这可以在没有 bind
的情况下解决。
您可以使用 arrow function together with the 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
的结果是一样的。
发生此问题是因为 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>
)
}
当从 React.createClass()
组件定义语法过渡到扩展 React.Component
.[=41 的 ES6 class 方式时,这个问题是我们大多数人首先遇到的事情之一=]
这是由于 React.createClass()
与 extends React.Component
.
中的 this
上下文差异造成的
使用 React.createClass()
会自动正确绑定 this
上下文(值),但使用 ES6 classes 时情况并非如此。当使用 ES6 方式(通过扩展 React.Component
)时,this
上下文默认为 null
。 class 的属性不会自动绑定到 React class(组件)实例。
解决此问题的方法
我总共知道4种通用方法。
在 class 构造函数 中绑定您的函数。许多人认为这是一种完全避免接触 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 包,它将 class 的方法绑定到 this
的正确实例,即使这些方法是分离的。包 在方法之前使用 @autobind
将 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 足够聪明,可以让我们一次性绑定组件内的所有方法 class,就像方法 #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。但是,一旦您在 class 构造函数中获得大量绑定,我建议您探索方法 #4 中提到的帮助程序库之一。
其他
这与您遇到的问题无关,但您shouldn't overuse 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.
出于类似的目的,就像您需要的那样,使用 controlled component is the preferred way. I suggest you to consider using your Component state
。因此,您可以像这样简单地访问值:this.state.inputContent
.
您好,如果您不想绑定自己的函数调用。您可以使用 'class-autobind' 并像那样导入它
import autobind from 'class-autobind';
class test extends Component {
constructor(props){
super(props);
autobind(this);
}
不要在超级调用之前写自动绑定,因为它不会工作
如果你想在构造函数语法中保留绑定,你可以使用 proposal-bind-operator 并像下面这样转换你的代码:
constructor() {
this.changeContent = ::this.changeContent;
}
而不是:
constructor() {
this.changeContent = this.changeContent.bind(this);
}
更简单,不需要 bind(this)
或 fatArrow
。
我们需要将事件函数与构造函数中的组件绑定如下,
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
谢谢
您可以通过三种方式解决这个问题
1.Bind构造函数本身的事件函数如下
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调用时
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 使用箭头函数
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
我的建议是使用箭头函数作为属性
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) 并在道具到达时进行浅层检查
这个问题发生在react15.0之后,事件处理程序没有自动绑定到组件。因此,无论何时调用事件处理程序,您都必须手动将其绑定到组件。
有几种方法可以解决这个问题。但您需要知道哪种方法最好,为什么?通常,我们建议在 class 构造函数中绑定您的函数或使用箭头函数。
// 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 改变而重新渲染,否则可能会产生性能问题。
您可以按照以下步骤解决此问题
将 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
才能获得 class 中函数的实例。就像示例中的那样
<button onClick={this.sendContent.bind(this)}>Submit</button>
这样 this.state
将是有效的对象。
尽管之前的答案提供了解决方案的基本概述(即绑定、箭头函数、为您执行此操作的装饰器),但我还没有找到真正解释 为什么 这是必要的——在我看来这是混乱的根源,并导致不必要的步骤,例如不必要的重新绑定和盲目跟随别人所做的。
this
是动态的
为了理解这个具体情况,简单介绍一下this
是如何工作的。这里的关键是 this
是一个运行时绑定并且取决于当前的执行上下文。因此,为什么它通常被称为 "context"——提供有关当前执行上下文的信息,而您需要绑定的原因是因为您丢失了 "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!
可能会出乎意料地发现它记录了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 相关
这是一个 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 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
值。
为什么箭头函数属性有效
箭头函数 class 属性当前基于转换通过 Babel 工作:
handleClick = () => { /* Can use this just fine here */ }
变为:
constructor() {
super();
this.handleClick = () => {}
}
这是有效的,因为箭头函数 不 绑定它们自己的 this,而是使用它们封闭范围的 this
。在本例中,constructor
的 this
指向 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
,使其能够工作。
您使用的是 ES6,因此函数不会自动绑定到 "this" 上下文。您必须手动将函数绑定到上下文。
constructor(props) {
super(props);
this.changeContent = this.changeContent.bind(this);
}
您的函数需要绑定才能在事件处理程序中使用状态或道具
在 ES5 中,仅在构造函数中绑定事件处理函数,而不要直接在渲染中绑定。如果您直接在渲染中进行绑定,那么它会在您的组件每次渲染和重新渲染时创建一个新函数。所以你应该总是在构造函数中绑定它
this.sendContent = this.sendContent.bind(this)
在 ES6 中,使用箭头函数
当你使用箭头函数时,你不需要做绑定,你也可以远离范围相关的问题
sendContent = (event) => {
}
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"
解决方案:
- 在没有显式绑定的情况下,
bind
与方法名称可以使用胖箭头函数语法 ()=>{} 维护 [=12= 的上下文].
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
Other Solutions:
在 class 构造函数中绑定您的函数。
在 JSX 模板转义大括号中绑定您的函数 {}
{this.methodName.bind(这个)}
bind(this)
可以解决这个问题,如果您不喜欢使用 bind
.
,现在我们可以使用另外两种方法来实现这一点
1) 按照传统方式,我们可以在构造函数中使用bind(this)
,这样当我们使用函数作为JSX回调时,this
的上下文就是class本身。
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) 如果我们将函数定义为带箭头函数的class的attribute/field,我们就不需要再使用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 来演示这些代码片段,希望对您有所帮助。
如果有人能找到这个答案,
这是一种无需手动绑定即可绑定所有功能的方法
在构造函数中():
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)
}
}
并在你的构造函数中调用它:
bindAllFunctions({ bindTo: this })
我正在用 ES6(使用 BabelJS)编写一个简单的组件,但函数 this.setState
无法正常工作。
典型的错误包括
Cannot read property 'setState' of undefined
或
this.setState is not a function
你知道为什么吗?这是代码:
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.changeContent
在作为 onChange
prop 传递之前需要通过 this.changeContent.bind(this)
绑定到组件实例,否则函数体中的 this
变量不会引用组件实例,而是引用 window
。参见 Function::bind。
当使用 React.createClass
而不是 ES6 类 时,组件上定义的每个非生命周期方法都会自动绑定到组件实例。参见 Autobinding。
请注意,绑定一个函数会创建一个新函数。您可以直接在渲染中绑定它,这意味着每次组件渲染时都会创建一个新函数,或者在您的构造函数中绑定它,它只会触发一次。
constructor() {
this.changeContent = this.changeContent.bind(this);
}
对
render() {
return <input onChange={this.changeContent.bind(this)} />;
}
引用是在组件实例上设置的,而不是在 React.refs
上设置的:您需要将 React.refs.someref
更改为 this.refs.someref
。您还需要将 sendContent
方法绑定到组件实例,以便 this
引用它。
Morhaus 是正确的,但这可以在没有 bind
的情况下解决。
您可以使用 arrow function together with the 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
的结果是一样的。
发生此问题是因为 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>
)
}
当从 React.createClass()
组件定义语法过渡到扩展 React.Component
.[=41 的 ES6 class 方式时,这个问题是我们大多数人首先遇到的事情之一=]
这是由于 React.createClass()
与 extends React.Component
.
this
上下文差异造成的
使用 React.createClass()
会自动正确绑定 this
上下文(值),但使用 ES6 classes 时情况并非如此。当使用 ES6 方式(通过扩展 React.Component
)时,this
上下文默认为 null
。 class 的属性不会自动绑定到 React class(组件)实例。
解决此问题的方法
我总共知道4种通用方法。
在 class 构造函数 中绑定您的函数。许多人认为这是一种完全避免接触 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 包,它将 class 的方法绑定到
this
的正确实例,即使这些方法是分离的。包 在方法之前使用@autobind
将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 足够聪明,可以让我们一次性绑定组件内的所有方法 class,就像方法 #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。但是,一旦您在 class 构造函数中获得大量绑定,我建议您探索方法 #4 中提到的帮助程序库之一。
其他
这与您遇到的问题无关,但您shouldn't overuse 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.
出于类似的目的,就像您需要的那样,使用 controlled component is the preferred way. I suggest you to consider using your Component state
。因此,您可以像这样简单地访问值:this.state.inputContent
.
您好,如果您不想绑定自己的函数调用。您可以使用 'class-autobind' 并像那样导入它
import autobind from 'class-autobind';
class test extends Component {
constructor(props){
super(props);
autobind(this);
}
不要在超级调用之前写自动绑定,因为它不会工作
如果你想在构造函数语法中保留绑定,你可以使用 proposal-bind-operator 并像下面这样转换你的代码:
constructor() {
this.changeContent = ::this.changeContent;
}
而不是:
constructor() {
this.changeContent = this.changeContent.bind(this);
}
更简单,不需要 bind(this)
或 fatArrow
。
我们需要将事件函数与构造函数中的组件绑定如下,
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
谢谢
您可以通过三种方式解决这个问题
1.Bind构造函数本身的事件函数如下
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调用时
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 使用箭头函数
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
我的建议是使用箭头函数作为属性
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) 并在道具到达时进行浅层检查
这个问题发生在react15.0之后,事件处理程序没有自动绑定到组件。因此,无论何时调用事件处理程序,您都必须手动将其绑定到组件。
有几种方法可以解决这个问题。但您需要知道哪种方法最好,为什么?通常,我们建议在 class 构造函数中绑定您的函数或使用箭头函数。
// 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 改变而重新渲染,否则可能会产生性能问题。
您可以按照以下步骤解决此问题
将 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
才能获得 class 中函数的实例。就像示例中的那样
<button onClick={this.sendContent.bind(this)}>Submit</button>
这样 this.state
将是有效的对象。
尽管之前的答案提供了解决方案的基本概述(即绑定、箭头函数、为您执行此操作的装饰器),但我还没有找到真正解释 为什么 这是必要的——在我看来这是混乱的根源,并导致不必要的步骤,例如不必要的重新绑定和盲目跟随别人所做的。
this
是动态的
为了理解这个具体情况,简单介绍一下this
是如何工作的。这里的关键是 this
是一个运行时绑定并且取决于当前的执行上下文。因此,为什么它通常被称为 "context"——提供有关当前执行上下文的信息,而您需要绑定的原因是因为您丢失了 "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!
可能会出乎意料地发现它记录了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 相关
这是一个 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 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
值。
为什么箭头函数属性有效
箭头函数 class 属性当前基于转换通过 Babel 工作:
handleClick = () => { /* Can use this just fine here */ }
变为:
constructor() {
super();
this.handleClick = () => {}
}
这是有效的,因为箭头函数 不 绑定它们自己的 this,而是使用它们封闭范围的 this
。在本例中,constructor
的 this
指向 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
,使其能够工作。
您使用的是 ES6,因此函数不会自动绑定到 "this" 上下文。您必须手动将函数绑定到上下文。
constructor(props) {
super(props);
this.changeContent = this.changeContent.bind(this);
}
您的函数需要绑定才能在事件处理程序中使用状态或道具
在 ES5 中,仅在构造函数中绑定事件处理函数,而不要直接在渲染中绑定。如果您直接在渲染中进行绑定,那么它会在您的组件每次渲染和重新渲染时创建一个新函数。所以你应该总是在构造函数中绑定它
this.sendContent = this.sendContent.bind(this)
在 ES6 中,使用箭头函数
当你使用箭头函数时,你不需要做绑定,你也可以远离范围相关的问题
sendContent = (event) => {
}
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"
解决方案:
- 在没有显式绑定的情况下,
bind
与方法名称可以使用胖箭头函数语法 ()=>{} 维护 [=12= 的上下文].
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
Other Solutions:
在 class 构造函数中绑定您的函数。
在 JSX 模板转义大括号中绑定您的函数 {} {this.methodName.bind(这个)}
bind(this)
可以解决这个问题,如果您不喜欢使用 bind
.
1) 按照传统方式,我们可以在构造函数中使用bind(this)
,这样当我们使用函数作为JSX回调时,this
的上下文就是class本身。
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) 如果我们将函数定义为带箭头函数的class的attribute/field,我们就不需要再使用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 来演示这些代码片段,希望对您有所帮助。
如果有人能找到这个答案, 这是一种无需手动绑定即可绑定所有功能的方法
在构造函数中():
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)
}
}
并在你的构造函数中调用它:
bindAllFunctions({ bindTo: this })