一、通过案例浅谈 react 开发中 this 指向问题
效果图:
点击计数案例:
import React, { Component } from 'react'
// 需求:点击按钮让数字在原来的基础上加 1
// 1. 给按钮绑定点击事件
// !2. 在事件回调里面,获取原来的数字
// 3. 加 1
export default class App extends Component {
state = {
// 提取到状态里面的 count 可以被改变,改变之后视图会更新
count: 0,
}
handleClick() {
// 类当中的方法默认就是开启了严格模式
// 代码打包之后其实也会严格模式
console.log(this) // undefined,不是实例,如果是实例该多好啊,为什么是 undefined 呢?
// console.log(this.state.count)
}
render() {
return (
<div style={{ textAlign: 'center' }}>
<h2>计数器:{this.state.count}</h2>
{/* 一定是 this.handleClick */}
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
怎么解决呢? 解释一下上述 this 为什么是 undefined
<script>
class App {
handleClick() {
// 方法中的 this 谁调用就是谁
console.log(this) // this
}
}
const a = new App()
// a.handleClick()
const onClick = a.handleClick
onClick() // window.onClick()
</script>
首先解决 this 的指向问题
handleClick() {
// 方法中的 this 谁调用就是谁
// 期望 this => 实例
console.log(this)
return undefined
}
render() {
return (
<div style={{ textAlign: 'center' }}>
<h2>计数器:{this.state.count}</h2>
{/* 一旦加了括号,表示是 this 调用的,为 render 中的 this 就是实例,说明是实例调用的 */}
{/* 这样确实解决了 this 指向问题,但是又引来一个新问题,就是点击按钮的时候执行的是 undefined */}
<button onClick={this.handleClick()}>+1</button>
</div>
)
}
#1 解决this指向的第1种方式
import React, { Component } from 'react'
// 需求:点击按钮让数字在原来的基础上加 1
// 1. 给按钮绑定点击事件
// !2. 在事件回调里面,获取原来的数字
// 3. 加 1
export default class App extends Component {
state = {
count: 0,
}
// 高阶函数:返回函数的函数,或参数是函数的函数,[].map((item) => {})
// 函数柯里化:通过函数调用继续返回函数的形式,实现多次接收参数最后统一处理的编码形式
handleClick(num) {
// this => 实例
return (e) => {
// num e => 统一处理
// 箭头函数没有自己的 this,指向外部,外部的 this 就是实例
console.log(this.state.count)
}
}
render() {
return (
<div style={{ textAlign: 'center' }}>
<h2>计数器:{this.state.count}</h2>
{/* handleClick() 调用,返回一个箭头函数 */}
<button onClick={this.handleClick(1)}>+1</button>
</div>
)
}
}
#2 解决this指向的第2种方式
import React, { Component } from 'react'
// 需求:点击按钮让数字在原来的基础上加 1
// 1. 给按钮绑定点击事件
// !2. 在事件回调里面,获取原来的数字
// 3. 加 1
export default class App extends Component {
state = {
count: 0,
}
handleClick() {
// 实例调用的 => this => 实例
console.log(this.state.count)
}
render() {
return (
<div style={{ textAlign: 'center' }}>
<h2>计数器:{this.state.count}</h2>
<button onClick={(e) => this.handleClick()}>+1</button>
</div>
)
}
}
#3 解决this指向的第3种方式
import React, { Component } from 'react'
// 需求:点击按钮让数字在原来的基础上加 1
// 1. 给按钮绑定点击事件
// !2. 在事件回调里面,获取原来的数字
// 3. 加 1
export default class App extends Component {
state = {
count: 0,
}
handleClick(a, b, e) {
// 最后对应不上的那个形参就是事件对象
console.log(this.state.count, a + b, e)
}
render() {
return (
<div style={{ textAlign: 'center' }}>
<h2>计数器:{this.state.count}</h2>
{/* bind 谁,handleClick 中的 this 就是谁,bind 的 this 是实例,所以 handleClick 中的 this 就是实例 */}
{/* bind 返回的是一个新函数,所以点击按钮的时候执行的就是这个新函数,而新函数和 handleClick 公用的是一个函数体,所以执行的就是 handleClick 里面的代码 */}
<button onClick={this.handleClick.bind(this, 1, 2)}>+1</button>
</div>
)
}
}
#4 解决this指向的第4种方式
import React, { Component } from 'react'
// 需求:点击按钮让数字在原来的基础上加 1
// 1. 给按钮绑定点击事件
// !2. 在事件回调里面,获取原来的数字
// 3. 加 1
export default class App extends Component {
state = {
count: 0,
}
// 实例属性,装的是一个箭头函数
// 如果说我有办法,证明这儿的 this 是实例,是不是就能说明 handleClick 箭头函数里面的 this 是实例呀~
handleClick = () => {
console.log(this.state.count)
}
render() {
return (
<div style={{ textAlign: 'center' }}>
<h2>计数器:{this.state.count}</h2>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
证明箭头函数外部的this就是实例
class App {
// 把 this 赋值给了实例属性 temp
temp = this // #1
// 证明这儿的 this 是实例
handleClick = () => {
console.log(this)
}
}
const a = new App()
console.log(a.temp === a) // true,就是说明 #1 处的 this 就是实例
#5 解决this指向的第5种方式
import React, { Component } from 'react'
// 需求:点击按钮让数字在原来的基础上加 1
// 1. 给按钮绑定点击事件
// !2. 在事件回调里面,获取原来的数字
// 3. 加 1
export default class App extends Component {
constructor() {
super()
this.state = {
count: 0,
}
// 1. 右边的 this.handleClick.bind(this),会优先从实例上面找 handleClick,发现实例上面没有 handleClick
// 2. 所以找到了原型上的 handleClick
// 3. 然后通过 bind 把原型上的 handleClick 里面的 this 变成了这儿 this(实例)
// 4. 同时把返回的新函数给了实例上面的 handleClick 属性
// 5. 此时实例上面的 handleClick 属性对应的新方法和原型上面的 handleClick 公用一个函数体
// 6. 意味着调用实例上面的 handleClick 的时候也会执行原型上面的 handleClick 函数体
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
console.log(this.state.count)
}
render() {
return (
<div style={{ textAlign: 'center' }}>
<h2>计数器:{this.state.count}</h2>
<button onClick={this.handleClick}>+1</button>
</div>
)
}
}
完成案例点击计数
handleClick = () => {
// 数据确实变了,但视图不是响应式的
// this.state.count += 1
// !注意:setState() 的操作是合并,不会影响没有操作到的数据
this.setState({
// key 表示要修改的数据
// value 表示要变成的结果
count: this.state.count + 1,
})
}