- 问题起因:
React16 中还是用类组件的方式,写了下面的一个 demo 产生了一个问题
<body>
<div id="app"></div>
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
message: "Hello World"
};
}
render() {
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.changeText}>改变文本</button>
</div>
)
}
changeText() {
this.setState({
message: "Hello React"
})
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
</body>
此时点击按钮的时候,发现报错: Cannot read properties of undefined (reading 'setState')
其实就是 changeText 函数中的 this 丢失了。this 成了 undefined
- 问题排查
- Javascript 函数中的 this 是谁去调用了函数 this 就指向谁。创建了组件实例
<App />所以 constructor 和 render 中的 this 是指向组件实例的。render 函数是实例创建好之后,通过实例对象调用的,所以this是指向实例对象的。 - changeText 函数赋值给了 button 的点击事件,监听点击,监听函数中的this本应该是节点对象,如下:
<body>
<button id="create">创建</button>
<script>
var createBtn = document.getElementById('create')
function add(){
console.log(this) // <button id="create">创建</button>
}
createBtn.onclick = add
</script>
</body>
- 但是这里为什么是 undefined 呢,因为在类中开启了严格模式,,所以是 undefined,如下:
<script type="text/babel">
class App {
show() {
console.log(this);
}
}
const app = new App();
let temp = app.show;
app.show(); // App {}: Object
temp(); // undefined
</script>
解决方式:
- bind (显示绑定 this)
class App extends React.Component {
constructor() {
super();
this.state = {
message: "Hello World"
};
this.changeText = this.changeText.bind(this)
}
render() {
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.changeText}>改变文本</button>
</div>
)
}
changeText() {
this.setState({
message: "Hello React"
})
}
}
ReactDOM.render(<App />, document.getElementById("app"));
- 箭头函数(changeText 函数中没有 this,用了类中的 this)
- 箭头函数的 this 是外部作用域的 this ,与函数的调用方式无关。
- 箭头函数的this无法通过 call、apply、bind 修改。
- 箭头函数无法作为构造函数。
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
message: "Hello World"
};
}
render() {
return (
<div>
<h2>{this.state.message}</h2>
<button onClick={this.changeText}>改变文本</button>
</div>
)
}
changeText = () => {
this.setState({
message: "Hello React"
})
}
}
ReactDOM.render(<App />, document.getElementById("app"));
</script>
3. 直接传入一个箭头函数
btnClick() {
console.log("btnClick", this);
this.setState({ counter: 9999 });
}
render() {
return <button onClick={() => this.btnClick()}>按钮3</button>;
}
onClick 函数回调时,执行箭头函数,箭头函数再执行 btnClick 函数。this.btnClick()这里的 this 是组件实例对象。