React useState Hook问题:点击按钮无法获取最新state值求助
问题原因
你遇到的是React闭包陷阱:
- 你的
useEffect依赖项是空数组[],所以它只会在组件首次渲染时执行一次。 - 首次渲染时,
c的初始值是0,此时绑定的f1函数会捕获这个c=0的闭包环境。 - 之后你调用
setC(1)更新了状态,组件会重新渲染,生成新的f1函数(此时它能捕获到c=1),但旧的事件监听器还是绑定的首次渲染时的f1,所以点击按钮时输出的还是最初的0。
解决方案
方案1:用React原生的事件绑定(推荐)
完全不用手动操作DOM添加监听器,直接在按钮上通过onClick绑定事件,React会自动帮你处理更新,每次渲染都会绑定最新的f1:
import React, { useState, useEffect } from 'react'; export function App(props) { const [c, setC] = useState(0); useEffect(() => { setC(1); }, []); const f1 = () => { console.log(c); } return ( <div className='App'> <h1>Hello React.</h1> <h2>Start editing to see some magic happen!</h2> <button onClick={f1} /> </div> ); } console.log('Hello console')
方案2:手动绑定事件时处理闭包(适合必须操作DOM的场景)
如果一定要手动绑定DOM事件,需要在useEffect中清理旧监听器,并把c加入依赖项,这样c更新时会重新绑定最新的f1:
import React, { useState, useEffect } from 'react'; export function App(props) { const [c, setC] = useState(0); useEffect(() => { const button = document.getElementById("b1"); if (!button) return; // 绑定最新的f1 button.addEventListener("click", f1); // 清理函数:组件卸载或依赖变化时移除旧监听器 return () => { button.removeEventListener("click", f1); }; }, [c]); // 把c加入依赖,c变化时重新执行effect useEffect(() => { setC(1); }, []); const f1 = () => { console.log(c); } return ( <div className='App'> <h1>Hello React.</h1> <h2>Start editing to see some magic happen!</h2> <button id="b1" /> </div> ); } console.log('Hello console')
方案3:用useRef保存最新状态
如果不想让useEffect依赖c,可以用useRef来保存最新的c值,因为ref的current属性是可变的,闭包能访问到最新值:
import React, { useState, useEffect, useRef } from 'react'; export function App(props) { const [c, setC] = useState(0); const cRef = useRef(c); // 每次c更新时同步到ref useEffect(() => { cRef.current = c; }, [c]); useEffect(() => { setC(1); const b1 = document.getElementById("b1"); b1.addEventListener("click", f1); return () => { b1.removeEventListener("click", f1); }; }, []); const f1 = () => { console.log(cRef.current); // 访问最新的c值 } return ( <div className='App'> <h1>Hello React.</h1> <h2>Start editing to see some magic happen!</h2> <button id="b1" /> </div> ); } console.log('Hello console')
内容的提问来源于stack exchange,提问作者EdwinC
相关产品推荐
相关产品推荐

