立即计算 & 懒计算

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 

参考

Last Updated:
Contributors: Vsnoy