You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React状态已更新,但testFunc中console.log始终返回初始值0求解释

问题原因与解决方案

核心原因

这是闭包捕获旧状态结合useEffect依赖缺失导致的问题,拆解来看:

  • 闭包捕获特性:useCallback创建的testFunc会捕获当前作用域的test值。组件首次渲染时test是0,此时生成的testFunc里的test就固定是0;只有当test变化时,useCallback才会生成新的函数,捕获新的test值。
  • useEffect依赖问题:你的useEffect依赖是空数组[],只会在组件首次渲染时执行一次,给window绑定的scroll事件监听是初始版本的testFunc——也就是那个捕获了test=0的函数。后续test更新时,虽然会生成新的testFunc,但useEffect不会重新执行,所以scroll事件始终调用旧的函数,console.log(test)自然一直输出0。
  • 为什么状态能正常更新?因为你用了函数式更新setTest(prev => prev + 1),这种方式不依赖当前作用域的test值,而是直接取React状态的最新快照,所以状态可以正常累加。

解决方案

方案一:给useEffect添加正确依赖,自动更新事件绑定

让useEffect在testFunc更新时重新绑定scroll事件,同时添加清理函数防止内存泄漏:

'use client'

import React, { useCallback, useEffect, useState } from 'react'

type Props = {}

const TestPage = (props: Props) => {
  const [test, setTest] = useState<number>(0)

  const testFunc = useCallback(() => {
    console.log(test)
    setTest(prev => prev + 1)
  }, [test])

  const handleClick = () => console.log(test)

  useEffect(() => {
    window.addEventListener('scroll', testFunc)
    // 清理函数:组件卸载或依赖更新时移除旧监听
    return () => {
      window.removeEventListener('scroll', testFunc)
    }
  }, [testFunc]) // 依赖testFunc,test变化时重新绑定

  return (
    <>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
    </>
  )
}

export default TestPage

方案二:用useRef保存最新函数,避免频繁绑定/解绑

如果不想每次test变化都重新绑定事件,可以用useRef存储最新的testFunc,让scroll事件始终调用最新版本:

'use client'

import React, { useCallback, useEffect, useRef, useState } from 'react'

type Props = {}

const TestPage = (props: Props) => {
  const [test, setTest] = useState<number>(0)
  const testFuncRef = useRef<() => void>(() => {})

  const testFunc = useCallback(() => {
    console.log(test)
    setTest(prev => prev + 1)
  }, [test])

  // 每次testFunc更新时,同步到ref
  useEffect(() => {
    testFuncRef.current = testFunc
  }, [testFunc])

  const handleClick = () => console.log(test)

  useEffect(() => {
    const handleScroll = () => {
      testFuncRef.current()
    }
    window.addEventListener('scroll', handleScroll)
    return () => {
      window.removeEventListener('scroll', handleScroll)
    }
  }, [])

  return (
    <>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
    </>
  )
}

export default TestPage

方案三:简化逻辑,直接在事件处理中更新状态

如果你的需求只是滚动时更新状态,不需要在滚动事件里直接访问当前test值,可以去掉useCallback,直接用函数式更新,同时单独监听test变化来打印:

'use client'

import React, { useEffect, useState } from 'react'

type Props = {}

const TestPage = (props: Props) => {
  const [test, setTest] = useState<number>(0)

  // 单独监听test变化打印
  useEffect(() => {
    console.log(test)
  }, [test])

  const handleClick = () => console.log(test)

  useEffect(() => {
    const handleScroll = () => {
      setTest(prev => prev + 1)
    }
    window.addEventListener('scroll', handleScroll)
    return () => {
      window.removeEventListener('scroll', handleScroll)
    }
  }, [])

  return (
    <>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
      <div className='h-screen bg-black' onClick={handleClick}>TestPage</div>
    </>
  )
}

export default TestPage

内容的提问来源于stack exchange,提问作者William.D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 06:17:32