我正在ES6中编写一个简单的组件(使用BabelJS),并函数此。setState不工作。
典型的错误包括
无法读取undefined的属性“setState”
or
这一点。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
此问题发生在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将不会因为新的函数道具的改变而重新渲染,或者可能会产生性能问题。
这个问题是我们大多数人在从React.createClass()组件定义语法过渡到扩展React.Component的ES6类方式时最先经历的事情之一。
这是由React.createClass()与extends React.Component中的上下文差异引起的。
使用React.createClass()将自动正确绑定此上下文(值),但在使用ES6类时并非如此。当以ES6的方式(通过扩展React.Component)执行时,此上下文默认为空。类的属性不会自动绑定到React类(组件)实例。
解决该问题的方法
我一共知道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 this value. However, the arrow function does not create its own this context, so this has 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 Decorator is an NPM package which binds methods of a class to the correct instance of this, even when the methods are detached. The package uses @autobind before methods to bind this to the correct reference to 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 Autobind is 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 binds the 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.
建议
如果我是你,我会坚持第一种方法。但是,一旦您的类构造函数中有大量的绑定,我建议您研究方法#4中提到的helper库之一。
其他
这与你的问题无关,但你不应该过度使用裁判。
你的第一个倾向可能是使用refs在你的应用中“让事情发生”。如果是这种情况,花点时间,更批判性地思考在组件层次结构中应该在哪里拥有状态。
对于类似的目的,就像您需要的一样,使用受控组件是首选的方法。我建议您考虑使用Component状态。所以,你可以像这样访问这个值:this.state. inputcontent。
您的函数需要绑定,以便使用事件处理程序中的状态或道具
在ES5中,只在构造函数中绑定事件处理函数,而不直接在呈现中绑定。如果你直接在渲染中绑定,那么每次你的组件渲染和重新渲染时,它都会创建一个新函数。所以你应该总是在构造函数中绑定它
this.sendContent = this.sendContent.bind(this)
在ES6中,使用箭头函数
当你使用箭头函数时,你不需要做绑定,你也可以远离范围相关的问题
sendContent = (event) => {
}
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"