react函数组件中的闭包问题

48 阅读1分钟

问题

需求是每秒请求日志信息 如果连续5s不变则进行下一步

function Comp() {
  const [log, setLog] = useState()
  useEffect(() => {
    let counter = 0
    let timer
    const cleanup = () => clearInterval(timer)
    const fetchLog = () => {
      getLog().then(newLog => {
        if (log !== newLog) {
          counter = 0
          setLog(newLog)
          return
        }
        ++counter
        if (counter >= 5) {
          cleanup()
          // 下一步操作
        }
      })
    }
    fetchLog()
    timer = setInterval(fetchLog, 1000)
    return cleanup
  }, [])
  return ...
}

实测的时候发现始终无法进行下一步 即使调整getLog函数的值始终一致
在适当的位置打印后 发现参与比较的log始终是undefined
此时我理解了bug的原因:useEffect始终调用了第一次接受的setup函数,而参与比较的log来自于该函数的闭包,它的值是不会更新的.
每次调用组件函数得到的state可能与上一次不同,这种不同构成了组件state的可变性,但每次调用组件函数取得的值本身是不会变的.这个还是挺容易出错了,需要自己小心

解决

使用回调函数作为setState的参数,该函数始终能得到最新的state;也可以用一个ref,每次setState时都为ref赋值

getLog().then(newLog => {
    setLog(log=>{
        if(log!==newLog)
        //...
    })
})