如果你正在使用React类组件,你可能会发现自己被'this.state'所困扰,undefined 。
问题所在
你可能正试图在事件处理程序中调用一个实例方法。考虑下面的例子,一个基本的计数器,我们可以增减。
class App extends React.Component {
constructor() {
super();
this.state = {
count: 0,
};
}
increment() {
this.setState({ count: this.state.count + 1 });
}
decrement() {
this.setState({ count: this.state.count - 1 });
}
render() {
return (
<div className="App">
<h1>Counter</h1>
<div>{this.state.count}</div>
<button onClick={this.increment}>Increment</button>
<button onClick={this.decrement}>Decrement</button>
</div>
);
}
}
export default App;
当我们点击增量或减量按钮时,我们会得到一个错误:Cannot read property 'count' of undefined 。我们的控制台会显示,确实看起来this.state 是未定义的。
那么,为什么会出现这种情况呢?
原因是
使用ES6类的React组件不会自动绑定this 到非React.Component方法。这意味着我们无法保证increment 和decrement 方法中的this 是指 React 类组件的实例。
解决方案
解决方案1:在构造函数中绑定
我们有几个不同的选择。最流行的,也是我最喜欢的,是在constructor 方法中立即将我们的方法与React组件实例绑定。
这个实现的结果看起来像。
class App extends React.Component {
constructor() {
super();
this.state = {
count: 0,
};
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
}
increment() {
this.setState({ count: this.state.count + 1 });
}
decrement() {
this.setState({ count: this.state.count - 1 });
}
render() {
return (
<div className="App">
<h1>Counter</h1>
<div>{this.state.count}</div>
<button onClick={this.increment}>Increment</button>
<button onClick={this.decrement}>Decrement</button>
</div>
);
}
}
export default App;
解决方案2:在事件处理程序中绑定
由于我们只调用一次this.increment 和this.decrement ,我们实际上可以只在button 事件处理程序本身中传递这些函数的绑定版本。这样做可以,但如果你要在多个地方调用这些方法,很快就会变得效率低下,而且容易出错。
下面是这种方法的样子。
class App extends React.Component {
constructor() {
super();
this.state = {
count: 0,
};
}
increment() {
this.setState({ count: this.state.count + 1 });
}
decrement() {
this.setState({ count: this.state.count - 1 });
}
render() {
return (
<div className="App">
<h1>Counter</h1>
<div>{this.state.count}</div>
<button onClick={this.increment.bind(this)}>Increment</button>
<button onClick={this.decrement.bind(this)}>Decrement</button>
</div>
);
}
}
export default App;
解决方案3:将方法改为箭头函数类属性
我们的最后一个选择是将我们的increment 和decrement 方法改为箭头函数类属性。这个方案表面上看起来很诱人,但我们在看完实现后会讨论为什么它是我最不喜欢的方案。
class App extends React.Component {
constructor() {
super();
this.state = {
count: 0,
};
}
increment = () => {
this.setState({ count: this.state.count + 1 });
};
decrement = () => {
this.setState({ count: this.state.count - 1 });
};
render() {
return (
<div className="App">
<h1>Counter</h1>
<div>{this.state.count}</div>
<button onClick={this.increment}>Increment</button>
<button onClick={this.decrement}>Decrement</button>
</div>
);
}
}
export default App;
这确实有效,但最大的问题是,每次创建组件的新实例时,都会创建increment 和decrement 。换句话说,它们存在于实例上而不是构造函数上。与存在于原型上的属性相比,这最终在理论上是低效的。
总结
在这篇文章中,我们发现了为什么React类组件可能会出现未定义的状态,以及解决这个问题的几种不同方法。