use*_*212 201 javascript ecmascript-6 reactjs babeljs
我在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
Run Code Online (Sandbox Code Playgroud)
Ale*_*erg 238
this.changeContent需要this.changeContent.bind(this)在作为onChangeprop 传递之前通过组件实例绑定,否则this函数体中的变量将不会引用组件实例而是引用window.请参见Function :: bind.
使用React.createClass而不是ES6类时,组件上定义的每个非生命周期方法都会自动绑定到组件实例.请参见自动绑定.
请注意,绑定函数会创建一个新函数.您可以直接在render中绑定它,这意味着每次组件渲染时都会创建一个新函数,或者在构造函数中绑定它,这只会触发一次.
constructor() {
this.changeContent = this.changeContent.bind(this);
}
Run Code Online (Sandbox Code Playgroud)
VS
render() {
return <input onChange={this.changeContent.bind(this)} />;
}
Run Code Online (Sandbox Code Playgroud)
Refs在组件实例上设置,而不是在React.refs:您需要更改React.refs.someref为this.refs.someref.您还需要将sendContent方法绑定到组件实例,以便this引用它.
Kye*_*ica 99
Morhaus是正确的,但这可以在没有的情况下解决bind.
class SomeClass extends React.Component {
changeContent = (e) => {
this.setState({inputContent: e.target.value})
}
render() {
return <input type="text" onChange={this.changeContent} />;
}
}
Run Code Online (Sandbox Code Playgroud)
因为箭头函数是在构造函数的范围内声明的,并且因为箭头函数this从它们的声明范围维护,所以它都可以工作.这里的缺点是这些不是原型上的函数,它们都将使用每个组件重新创建.然而,这并不是一个缺点,因为bind结果是相同的.
Kal*_*sev 49
当从React.createClass()组件定义语法转换到ES6类扩展方式时,这个问题是我们大多数人经历的第一件事React.Component.
它是由vs中的this上下文差异引起的React.createClass()extends React.Component.
使用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>
);
}
}
Run Code Online (Sandbox Code Playgroud)将内容绑定到您的函数中.你仍然可以在一些教程/文章/等中找到这里和那里使用的这种方法,所以你知道它很重要.它与#1的概念相同,但请注意,绑定函数会为每次重新渲染创建一个新函数.
class SomeClass extends React.Component {
handleClick() {
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={this.handleClick.bind(this)}></button>
);
}
}
Run Code Online (Sandbox Code Playgroud)使用胖箭头功能.在箭头函数之前,每个新函数都定义了自己的this值.但是,箭头函数不会创建自己的this上下文,因此this具有React组件实例的原始含义.因此,我们可以:
class SomeClass extends React.Component {
handleClick() {
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={ () => this.handleClick() }></button>
);
}
}
Run Code Online (Sandbox Code Playgroud)
要么
class SomeClass extends React.Component {
handleClick = () => {
console.log(this); // the React Component instance
}
render() {
return (
<button onClick={this.handleClick}></button>
);
}
}
Run Code Online (Sandbox Code Playgroud)使用实用程序函数库自动绑定您的函数.有一些实用程序库,可以自动为您完成工作.以下是一些受欢迎的,仅举几例:
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>
);
}
}
Run Code Online (Sandbox Code Playgroud)
Autobind Decorator很聪明,可以让我们一次绑定组件类中的所有方法,就像方法#1一样.
Class 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>
);
}
}
Run Code Online (Sandbox Code Playgroud)
PS:其他非常相似的库是React Autobind.
如果我是你,我会坚持使用方法#1.但是,只要你在类构造函数中获得大量绑定,我就会建议你去探索方法#4中提到的一个帮助库.
这与你的问题无关,但你不应该过度使用refs.
你的第一个倾向可能是使用refs在你的应用程序中"让事情发生".如果是这种情况,请花一点时间,更关键地考虑组件层次结构中应该拥有状态的位置.
出于类似的目的,就像您需要的那样,使用受控组件是首选方式.我建议你考虑使用你的组件state.所以,你可以简单地访问这样的值:this.state.inputContent.
尽管之前的答案已经提供了解决方案的基本概述(即绑定、箭头函数、为您执行此操作的装饰器),但我还没有找到真正解释为什么这是必要的答案——我认为这是根本混乱,并导致不必要的步骤,例如不必要的重新绑定和盲目跟随他人的做法。
this 是动态的要了解这种特定情况,请简要介绍其this工作原理。这里的关键是这this是一个运行时绑定,取决于当前的执行上下文。因此,为什么它通常被称为“上下文”——提供有关当前执行上下文的信息,以及为什么需要绑定是因为你失去了“上下文”。但是让我用一个片段来说明这个问题:
const foobar = {
bar: function () {
return this.foo;
},
foo: 3,
};
console.log(foobar.bar()); // 3, all is good!
Run Code Online (Sandbox Code Playgroud)
在这个例子中,我们得到了3,正如预期的那样。但以这个例子为例:
const barFunc = foobar.bar;
console.log(barFunc()); // Uh oh, undefined!
Run Code Online (Sandbox Code Playgroud)
可能会意外地发现它记录了 undefined ——3去哪儿了?答案在于“上下文”,或者你如何执行一个函数。比较我们如何调用函数:
// Example 1
foobar.bar();
// Example 2
const barFunc = foobar.bar;
barFunc();
Run Code Online (Sandbox Code Playgroud)
注意区别。在第一个示例中,我们准确指定了bar方法1所在的位置——在foobar对象上:
foobar.bar();
^^^^^^
Run Code Online (Sandbox Code Playgroud)
但是在第二个中,我们将方法存储到一个新变量中,并使用该变量调用该方法,而没有明确说明该方法实际存在的位置,从而丢失了上下文:
barFunc(); // Which object is this function coming from?
Run Code Online (Sandbox Code Playgroud)
这就是问题所在,当您将方法存储在变量中时,有关该方法所在位置(执行该方法的上下文)的原始信息丢失了。如果没有这些信息,在运行时,JavaScript 解释器就无法绑定正确的this- 没有特定的上下文,this无法按预期工作2。
这是一个遇到问题的 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>
);
}
Run Code Online (Sandbox Code Playgroud)
但是为什么,以及上一节与此有何关系?这是因为他们遭受了同一问题的抽象。如果你看看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
Run Code Online (Sandbox Code Playgroud)
因此,当您这样做时onClick={this.handleClick},该方法this.handleClick最终会分配给变量listener3。但是现在你看到问题出现了——因为我们已经赋值this.handleClick给listener,我们不再确切地指定handleClick来自哪里!从 React 的角度来看,listener只是一些函数,不附加到任何对象(或者在这种情况下,React 组件实例)。我们丢失了上下文,因此解释器无法推断出this要在内部使用的值 handleClick。
您可能想知道,如果解释器this在运行时决定值,为什么我可以绑定处理程序使其工作?这是因为您可以使用Function#bind来保证this运行时的值。这是通过this在函数上设置内部绑定属性来完成的,允许它不推断this:
this.handleClick = this.handleClick.bind(this);
Run Code Online (Sandbox Code Playgroud)
当这一行被执行时,大概是在构造函数中,当前this被捕获(React 组件实例)并设置为一个this全新函数的内部绑定,从Function#bind. 这确保this在运行时计算时,解释器不会尝试推断任何内容,而是使用提供的this您值。
箭头函数类属性目前通过基于转译的 Babel 工作:
handleClick = () => { /* Can use this just fine here */ }
Run Code Online (Sandbox Code Playgroud)
变成:
constructor() {
super();
this.handleClick = () => {}
}
Run Code Online (Sandbox Code Playgroud)
这是因为箭头函数不绑定它们自己的 this ,而是占用this它们封闭范围的the 。在这种情况下,指向 React 组件实例的 constructor 's this,从而为您提供正确的this. 4
1我使用“方法”来指代应该绑定到对象的函数,而使用“函数”来指代没有绑定到对象的函数。
2在第二个代码段中,记录 undefined 而不是 3,因为当无法通过特定上下文确定时,this默认为全局执行上下文(window当不是严格模式时,否则为 else undefined)。并且在示例window.foo中不存在因此产生未定义。
3如果您深入了解事件队列中的事件是如何执行的,invokeGuardedCallback则在侦听器上调用。
4它实际上要复杂得多。React 内部尝试将Function#apply侦听器用于自己的用途,但这不起作用箭头函数,因为它们根本不绑定this。这意味着,当this实际评估箭头函数时,this会解析模块当前代码的每个执行上下文的每个词法环境。最终解析为具有this绑定的执行上下文是构造函数,它有一个this指向当前 React 组件实例的指针,允许它工作。