立即计算 & 懒计算
WARNING
下述研究都是建立在 Hooks 函数组件内,且仅适用于合成事件和钩子函数( useEffect )内。
立即计算切换懒计算好说,懒计算切换立即计算的时机,下述规则与实际还有些出入,后续待研究。
立即计算
React 默认采用的是立即计算策略。
- 使用场景
- 上个
state为初始state - 上个
state相较之前未发生变化
- 上个
- 具体规则
- 如果
state值不变,则不会触发re-render - 如果
state值发生变化,则立即转到懒计算策略
- 如果
function Counter() {
const [counter, setCounter] = useState(0)
console.log('Counter.render')
return (
<>
<p>{`couter: ${counter}`}</p>
<button onClick={
() => {
console.log('click start')
// 上个 state 是初始 state,采用立即计算
setCounter(prev => {
console.log(`update 0, prev ${prev}`)
return 0
})
// 上个 state 相较之前未发生变化,继续采用立即计算
setCounter(prev => {
console.log(`update 1, prev ${prev}`)
return 1
})
// 上个 state 相较之前发生变化,立即计算转变成懒计算
setCounter(prev => {
console.log(`update 2, prev ${prev}`)
return 2
})
setCounter(prev => {
console.log(`update 3, prev ${prev}`)
return 3
})
console.log('click end')
}
}>
setCounter
</button>
</>
)
}
// 点击 setCounter 后输出
click start
update 0, prev 0
update 1, prev 0
click end
update 2, prev 1
update 3, prev 2
Counter.render
懒计算
除上述提到的两种场景之外,都是懒计算。
- 具体规则
- 先渲染,再执行各更新队列内的更新函数
- 更新队列执行先后顺序,取决于
useState声明顺序
function Counter() {
// 懒计算根据 useState 声明先后顺序,依次执行对应 state 的更新队列
// 这里 counter1 对应的 useState 在 counter2 之前
// 则先执行 counter1 对应的更新队列,再执行 counter2 对应的更新队列
const [counter1, setCounter1] = useState(0)
const [counter2, setCounter2] = useState(0)
console.log('Counter.render')
return (
<>
<p>{`couter1: ${counter1}`}</p>
<p>{`couter2: ${counter2}`}</p>
<button onClick={
() => {
console.log('click start')
// 上个 state 是初始 state,采用立即计算
setCounter1(prev => {
console.log(`setCounter1: update 1, prev ${prev}`)
return 1
})
// 上个 state 相较之前发生变化,立即计算转变成懒计算
// 以下更新函数都走懒计算,分 counter1、counter2 两个更新队列
// 执行顺序按照对应 useState 声明顺序
setCounter2(prev => {
console.log(`setCounter2: update 1, prev ${prev}`)
return 1
})
setCounter1(prev => {
console.log(`setCounter1: update 2, prev ${prev}`)
return 2
})
setCounter2(prev => {
console.log(`setCounter2: update 2, prev ${prev}`)
return 2
})
console.log('click end')
}
}>
setCounter
</button>
</>
)
}
// 点击 setCounter 后输出
click start
setCounter1: update1, prev 0
click end
setCounter1: update2, prev 1
setCounter2: update1, prev 0
setCounter2: update2, prev 1
Counter.render