我在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
答案 0 :(得分:222)
this.changeContent
需要在作为this.changeContent.bind(this)
道具传递之前通过onChange
绑定到组件实例,否则函数体中的this
变量将不会引用组件实例,但是window
。请参阅Function::bind。
使用React.createClass
而不是ES6类时,组件上定义的每个非生命周期方法都会自动绑定到组件实例。请参阅Autobinding。
请注意,绑定函数会创建一个新函数。您可以直接在render中绑定它,这意味着每次组件渲染时都会创建一个新函数,或者在构造函数中绑定它,这只会触发一次。
constructor() {
this.changeContent = this.changeContent.bind(this);
}
VS
render() {
return <input onChange={this.changeContent.bind(this)} />;
}
Refs在组件实例上设置,而不在React.refs
上:您需要将React.refs.someref
更改为this.refs.someref
。您还需要将sendContent
方法绑定到组件实例,以便this
引用它。
答案 1 :(得分:92)
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
导致同样的事情,这并不是一个缺点。
答案 2 :(得分:47)
当从React.createClass()
组件定义语法转换为扩展React.Component
的ES6类方式时,此问题是我们大多数人遇到的第一个问题。
这是由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>
);
}
}
将您的功能内联绑定。你仍然可以在一些教程/文章/等中找到这里和那里使用的这种方法,所以你了解它很重要。它与#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
方法将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。
你的第一个倾向可能是使用裁判来使事情发生并且#34;在你的应用程序中如果是这种情况,请花一点时间,更关键地考虑组件层次结构中应该拥有哪个状态。
出于类似目的,就像您需要的那样,使用controlled component是首选方式。我建议你考虑使用你的Component state
。因此,您只需访问以下值:this.state.inputContent
。
答案 3 :(得分:2)
尽管前面的答案提供了解决方案的基本概述(即绑定,箭头功能,为您完成此任务的装饰器),但我还没有遇到一个答案,它实际上解释了为什么是必要的-我认为这是造成混乱的根源,并导致不必要的步骤,例如不必要的重新绑定和盲目地跟随他人的工作。
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
到哪里去了?答案在于“上下文” 或您执行函数的方式。比较我们如何调用函数:
// Example 1
foobar.bar();
// Example 2
const barFunc = foobar.bar;
barFunc();
注意区别。在第一个示例中,我们确切地指定了bar
对象上的foobar
方法 1 所在的位置:
foobar.bar();
^^^^^^
但是在第二步中,我们将方法存储到一个新变量中,并使用该变量来调用该方法,而没有明确说明该方法实际存在的位置,因此失去上下文:
barFunc(); // Which object is this function coming from?
其中存在一个问题,当您将方法存储在变量中时,有关该方法所处位置(执行该方法的上下文)的原始信息会丢失。没有这些信息,在运行时,JavaScript解释器无法绑定正确的this
-在没有特定上下文的情况下,this
不能按预期 2 的方式工作。
这是一个受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工作:
handleClick = () => { /* Can use this just fine here */ }
成为:
constructor() {
super();
this.handleClick = () => {}
}
这是有效的,因为事实是箭头功能不将其自身绑定,而是采用其封闭范围的this
。在这种情况下,constructor
的{{1}}指向React组件实例,从而为您提供正确的this
。 4
1 我使用“方法”来指代应该绑定到对象的函数,而使用“方法”来指代不绑定到对象的函数。
2 在第二个代码段中,未定义记录而不是3,因为this
默认为全局执行上下文(this
不在严格模式下,否则为{{ 1}})无法通过特定上下文确定。并且在示例window
中不存在,因此产生未定义的结果。
3 如果深入了解如何执行事件队列中的事件,则会在侦听器上调用invokeGuardedCallback
。
4 实际上要复杂得多。 React内部尝试在监听器上使用undefined
以供自己使用,但这无法使用箭头功能,因为它们只是不绑定window.foo
。这意味着,当实际评估箭头函数中的Function#apply
时,this
会在模块当前代码的每个执行上下文的每个词法环境中进行解析。最终解析为具有this
绑定的执行上下文是构造函数,该构造函数的this
指向当前的React组件实例,从而使其能够工作。
答案 4 :(得分: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
由于
答案 5 :(得分:1)
你可以通过三种方式解决这个问题
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 :(得分:1)
我的建议是使用箭头功能作为属性
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)并在道具到达时进行浅层检查
答案 7 :(得分:1)
您可以按照以下步骤解决此问题
使用
更改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>
答案 8 :(得分:1)
我们必须用bind
this
来获得函数,以获取类中函数的实例。像这样
<button onClick={this.sendContent.bind(this)}>Submit</button>
这样this.state
将是有效的对象。
答案 9 :(得分:0)
如果有人能得到这个答案, 这是一种无需手动绑定所有功能即可绑定所有功能的方法
在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 })
答案 10 :(得分:0)
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来演示这些代码段,希望对您有所帮助。
答案 11 :(得分:0)
解决方案:
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)}
答案 12 :(得分:0)
亚历山大·基尔森伯格(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"
答案 13 :(得分:0)
您的函数需要绑定才能与事件处理程序中的状态或道具一起玩
在ES5中,仅将事件处理函数绑定在构造函数中,而不直接在render中绑定。如果您确实直接在render中绑定,那么每次您的组件渲染并重新渲染时,它都会创建一个新函数。因此,您应该始终将其绑定在构造函数中
this.sendContent = this.sendContent.bind(this)
在ES6中,使用箭头功能
使用箭头功能时,无需绑定,也可以避免与范围相关的问题
sendContent = (event) => {
}
答案 14 :(得分:0)
您正在使用ES6,因此函数不会自动绑定到“ this”上下文。您必须手动将函数绑定到上下文。
constructor(props) {
super(props);
this.changeContent = this.changeContent.bind(this);
}
答案 15 :(得分:0)
这个问题发生在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不会因为新功能道具的改变而受到影响,或者可能会产生性能问题。
答案 16 :(得分:0)
如果您想在构造函数语法中保留bind,可以使用proposal-bind-operator并转换代码,如下所示:
constructor() {
this.changeContent = ::this.changeContent;
}
而不是:
constructor() {
this.changeContent = this.changeContent.bind(this);
}
更简单,不需要bind(this)
或fatArrow
。
答案 17 :(得分:0)
您好,如果您不想关心绑定自己的函数调用。你可以使用'class-autobind'并像那样导入它
<ListView ItemsSource="{Binding Items}">
<behaviors:Interaction.Behaviors>
<behaviors:BehaviorCollection>
<behaviors:EventToCommand
BindingContext="{behaviors:RelativeContext MPage}"
EventName="ItemTapped"
CommandName="OnItemSelected"
PassEventArgument="True"/>
</behaviors:BehaviorCollection>
</behaviors:Interaction.Behaviors>
</ListView>
在超级调用之前不要写autobind因为它不起作用
答案 18 :(得分:0)
此问题正在发生,因为this.changeContent
和onClick={this.sendContent}
未绑定到组件实例的此。
还有另一个解决方案(除了在构造函数()中使用bind())使用ES6的箭头函数,它们共享周围代码的相同词法范围并维护 this ,所以您可以将render()中的代码更改为:
render() {
return (
<input type="text"
onChange={ () => this.changeContent() } />
<button onClick={ () => this.sendContent() }>Submit</button>
)
}