useState hooks, this.setState( ) 不及时更新问题
本篇文章是主要围绕着React16
中React.useState
更新展开话题来揭秘一些面试中常见问题,本文章是带着问题找答案的方式进行探讨。
我们先看下面一道题,先不要看答案,自己思考一下分别点击一次,a与b的值是多少,a:? b:?
const [a, setA] = useState(0)
const [b, setB] = useState(0)
const addTwice1 = () => {
setA(a => a + 1)
setA(a => a + 1)
}
const addTwice2 = () => {
setB(b + 1)
setB(b + 1)
}
return (
<div>
<div onClick={addTwice1}>a: {a}</div>
<div onClick={addTwice2} >b: {b}</div>
</div>
)
答案 a:2 b:1
再抛出一个问题? hooks中的useState怎么做到像setState的第二个参数(回调)取到更新后的值
this.setState('learn', (state) => console.log('这是回调函数,可以取到更新后的state'))
// 那么我们在react hooks中怎么能够实现同样的效果?
const [name, setName] = useState('')
setName('faith')
console.log(name) // 此刻打印出来的可能不是最新的值
如果我们看了上面的2道题,都比较迷惑,那么恭喜你,接下来我们从浅到深,通过6个例子学习
setCount(函数)
我们邀请明星技师111号
进行教学准备
// 例子 111号
// dsc: hooks,怎么拿到最新的更新state?
// 为什么执行完 addTwice1 count = 2
const addTwice1 = () => {
// 如果接受的是一个函数的话,我们可以理解【传入了一个接收上一次更新后state的函数】
setCount(prevCount => prevCount + 1)
setCount(preState => {
// 此刻打印的 preState 永远都是上一次 count更新的结果
console.log('faith=============preState', preState)
return preState + 1
})
}
总结:如果接受的是一个函数的话,我们可以理解【传入了一个接收上一次更新后state的函数】
setCount(常量)
我们邀请明星技师222号
进行教学准备
// 例子 222号
// 为什么执行完 addTwice2 count = 1
// dsc: React会将多个setState的调用合并为一个来执行,也就是说,当执行setState的时候,state中的数据并不会马上更新,
// addTwice2调用后结果是1应该很好理解,setCount是异步执行,第一个setCount执行时count是0所以执行的是setCount(1),
// 由于异步此时继续执行第二个setCount,此时count还是0,因为第一个setCount是异步所以count还未更新,所以此时第二个执行的也是setCount(1),所以最终的count是1;
const addTwice2 = () => {
setCount(count + 1)
setCount(count + 1)
}
setTimeout与setCount(函数)
我们邀请明星技师333号
进行教学准备
// 例子 333号
// 点击多少次,count就是几
// 原因是setCount接收的是函数,函数的入参一定是上一次count的更新
// preState 默认值= 0
const addTwice4 = () => {
setTimeout(() => {
setCount(preState => preState + 1)
}, 3000)
}
- 1、问,在3秒之内,快速立即触发addTwice4函数5次,结果count是什么?
- 2、问,在3秒之内,快速立即触发addTwice4函数5次,3秒到5秒之间,再触发2次addTwice4函数,那么最后count是多少?
答案:
1、count = 5
2、count = 7
解释:不分时间,因为我们使用了更新状态传入的是函数, 222号技术告诉我们,使用函数,【传入了一个接收上一次更新后state的函数】,因此我们一定要关注更新传入的是常量还是函数
setTimeout与setCount(常量)
我们邀请明星技师888号
进行教学准备
const addTwice3 = () => {
setTimeout(() => {
setCount(count + 1)
}, 3000)
}
- 1、问,在3秒之内,快速立即触发addTwice4函数5次,结果count是什么?
- 2、问,在3秒之内,快速立即触发addTwice4函数5次,3秒到5秒之间,再触发2次addTwice4函数,那么最后count是多少?
1、count = 1
2、count = 2
如果在 3s 内点击 任意次, count 都是1, 因为setCount是异步的,React会将多个setState的调用合并为一个来执行,也就是说,当执行setState的时候,state中的数据并不会马上更新。
setInterval与setCount(常量)
我们邀请明星技师777号
进行教学准备
useEffect(() => {
setInterval(() => {
// 会一直打印 faith=============, 但是 count 一直是 1
// 原因是形成了闭包
console.log('faith=============')
setCount(count + 1)
}, 2000)
}, [])
问,count 值是多少? 答案,一直是 1 思考,为什么888号, 再次点击还是能够触发更新,而777号的一直不可以更新了?
原因是 888再次点击会触发函数,重新创建 setTimeout形成新的作用域闭包 但是 setInterval 被创建就没被销毁,闭包一直存在
setInterval与setCount(函数)
我们邀请明星技师999号
进行教学准备
useEffect(() => {
setInterval(() => {
setCount(preState => preState + 1)
}, 2000)
}, [])
好了,以上通过各位技术的教学,大概了解。
hooks中的useState怎么做到像setState的第二个参数(回调)取到更新后的值
在React16 怎么模拟class组件的setState的第二个参数,回调函数拿到最新值?
一共有2种方式,自己实现一个自定义的useState hook
, 通过Promis结合useState实现
先看下效果图,在回到函数拿到最新的值。
自定hook方式
import { Button } from 'antd'
import React, { useState, useRef, useEffect } from 'react'
// 使用自定义hook结合useState,模拟class组件的setState的第二个参数-回调函数
const useSimulationCbWithUseStateCustom = (initState: any) => {
const [num, setNum] = useState(initState)
const cbRef = useRef<any>(null)
const setState = (state: any, cbFc: any) => {
setNum(state)
cbRef.current = cbFc
}
useEffect(() => {
typeof cbRef.current === 'function' && cbRef.current(num)
cbRef.current = null
}, [num])
return [num, setState]
}
const SimulationCbWithUseStateCustom = () => {
const [num, setNum] = useSimulationCbWithUseStateCustom(0)
const addNumCustom = () => {
setNum(num + 1, (res: number) => {
console.log('faith=============', res)
})
}
return (
<div>
SimulationCbWithUseStateCustom模式
<Button onClick={addNumCustom}>addNum</Button>num:{num}
</div>
)
}
export default SimulationCbWithUseStateCustom
Promise结合useState实现
import { Button } from 'antd'
import React, { useState } from 'react'
// 使用promise结合useState,模拟class组件的setState的第二个参数-回调函数
const SimulationCbWithUseState = () => {
const [num, setNum] = useState(0)
const addNum = () => {
new Promise((resolve, reject) => {
setNum(preNum => {
resolve(preNum + 1)
return preNum + 1
})
}).then(res => {
console.log('faith=============', res)
})
}
return (
<div>
SimulationCbWithUseStatePromise模式
<Button onClick={addNum}>addNum</Button>num:{num}
</div>
)
}
export default SimulationCbWithUseState
最后练习
const [count, setCount] = useState(0)
const addTwice1 = () => {
setCount(prevCount => prevCount + 1)
setCount(prevCount => prevCount + 1)
}
// 触发一次 addTwice1 count是多少?
// 2
const addTwice2 = () => {
setCount(count + 1)
setCount(count + 1)
}
// 触发一次 addTwice2 count是多少?
// 1
const addTwice3 = () => {
setTimeout(() => {
setCount(count + 1)
}, 3000)
}
// 在3秒内,立即触发5次,count是多少?
// 1
const addTwice4 = () => {
setTimeout(() => {
setCount(preState => preState + 1)
}, 3000)
}
// 在3秒内,立即触发5次,count是多少?
// 5
// 在3秒内,立即触发3次,在4秒立即触发3次,count是多少?
// 6
useEffect(() => {
setInterval(() => {
setCount(count + 1)
}, 2000)
}, [])
// count一直是1
useEffect(() => {
setInterval(() => {
setCount(preState => preState + 1)
}, 2000)
}, [])
// count2秒+1
转载自:https://juejin.cn/post/7081931395869704228