React函数组件useState首次点击取值undefined需两次点击问题排查
问题原因
你遇到的首次点击取到undefined、第二次点击才拿到正常值的问题,核心是对React状态更新机制和JS闭包的理解有偏差,具体点:
- React的状态更新是异步批处理的,调用
setGetCode不会立刻修改当前函数作用域内的GetCode常量,只有等组件完成重渲染后,新的渲染周期生成的作用域里,GetCode才是更新后的值 - 你写的
GetCodeMI没有返回Promise实例,在InsertInternalMov里链式调用.then本身就不符合Promise逻辑,就算接口请求完成触发了setGetCode,当前闭包里捕获的还是初始值(空字符串),取[0]自然是undefined - 第二次点击能拿到值,是因为第一次点击触发的状态更新已经完成重渲染,第二次点击执行时闭包里捕获的已经是上一次更新后的值了
可直接用的解决方案
方案1:直接返回接口结果(最适合你的场景)
如果只是要在请求完成后立刻拿到返回值做后续逻辑,不需要等状态更新,直接把接口结果从请求函数里return出来就行,状态该存还是存,供页面渲染使用:
const [GetCode, setGetCode] = useState(""); const GetCodeMI = () => { // 记得把Promise return出去 return Axios.get('http://localhost:3001/GetMICode') .then((response) => { const resData = response.data; setGetCode(resData); // 正常存状态,不影响页面渲染 return resData; // 直接把最新数据返回给调用方 }); } const InsertInternalMov = async () => { // 直接拿到接口返回的最新值,不存在闭包旧值问题 const latestCode = await GetCodeMI(); console.log(latestCode[0]); // 后续业务逻辑直接用latestCode即可 }
方案2:用useEffect监听状态变化
如果你的后续逻辑必须依赖状态变量本身,就把状态变更后要执行的逻辑放到useEffect里,监听对应状态的变化,状态更新完成后自动执行:
import { useState, useEffect } from 'react'; const [GetCode, setGetCode] = useState(""); const GetCodeMI = () => { Axios.get('http://localhost:3001/GetMICode') .then((response) => { setGetCode(response.data); }); } // 监听GetCode的变化 useEffect(() => { // 跳过初始空值的场景 if (GetCode?.length > 0) { console.log(GetCode[0]); // 这里写拿到code后要执行的业务逻辑 } }, [GetCode]); const InsertInternalMov = () => { GetCodeMI(); // 不要在这里直接读取GetCode,必然拿到旧值 }
核心机制提醒
- 用
const声明的React状态,在单次渲染的作用域里是永远不变的,setState本质是触发组件重渲染,在新的渲染周期里生成新的常量值,而不是修改当前作用域的已有变量 - 永远不要在调用
setState的同一个同步/异步上下文里,直接读取状态变量期待拿到更新后的值,这不是bug,是React的正常设计 - 对需要立刻使用的接口返回值,直接用接口返回的原始数据即可,不用绕一圈等状态更新再读,避免无意义的闭包问题
内容的提问来源于stack exchange,提问作者Herbert IN
相关产品推荐
相关产品推荐

