为什么React的useState hook赋值后无法立即获取最新值?如何修复?
问题原因
React的useState返回的状态更新方法是异步批量执行的,调用setResponse之后,React不会立刻修改当前作用域里selectedResponse变量的值,只会把状态更新请求加入队列,等当前整个onSubmitAnswer函数执行完成、触发组件重渲染之后,下一次渲染的selectedResponse才会拿到更新后的值。
你现在的代码中,setResponse调用后立刻打印selectedResponse、判断答案正误,用的都是当前这次渲染里的旧值,所以才会出现值滞后一次的现象。
修复方案
方案1:直接使用本次计算出的答案值做后续逻辑
你可以在坐标判断阶段就把本次计算出的答案存到本地变量里,后续打印、判断逻辑都用这个本地变量,需要同步到状态再调用setResponse即可,修改后的核心代码如下:
const onSubmitAnswer = () => { class Coordinate { x: number; y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } } const width: number = box0.current.getBoundingClientRect().width; const height: number = box0.current.getBoundingClientRect().height; const box0Rect = box0.current.getBoundingClientRect(); // 定义本地变量存储本次计算出的答案 let currentAnswer: string|null = null; const boxCoordinates: Array<Coordinate> = []; for(let i=0;i<4;i++) { const data = eval(`box${i+1}`); boxCoordinates.push(new Coordinate(data.current.getBoundingClientRect().x, data.current.getBoundingClientRect().y)); } if(Math.abs(boxCoordinates[0].x - box0Rect.x) < width && Math.abs(boxCoordinates[0].y - box0Rect.y) < height) { currentAnswer = "A"; } else if(Math.abs(boxCoordinates[1].x - box0Rect.x) < width && Math.abs(boxCoordinates[1].y - box0Rect.y) < height) { currentAnswer = "B"; } else if(Math.abs(boxCoordinates[2].x - box0Rect.x) < width && Math.abs(boxCoordinates[2].y - box0Rect.y) < height) { currentAnswer = "C"; } else if(Math.abs(boxCoordinates[3].x - box0Rect.x) < width && Math.abs(boxCoordinates[3].y - box0Rect.y) < height) { currentAnswer = "D"; } // 同步到state setResponse(currentAnswer); // 后续逻辑全部用本地的currentAnswer console.log(currentAnswer); if(correctResponse === currentAnswer) { console.log("ok"); } else { console.log("raspuns incorect"); } }
方案2:用useEffect监听状态变化执行后续逻辑
如果你必须等状态更新完成之后再执行逻辑,可以把打印、判断正误的逻辑抽到useEffect中,监听selectedResponse的变化,状态更新后会自动触发对应逻辑:
// 单独监听selectedResponse变化 useEffect(() => { if(selectedResponse === null) return; console.log(selectedResponse); if(correctResponse === selectedResponse) { console.log("ok"); } else { console.log("raspuns incorect"); } }, [selectedResponse, correctResponse]) const onSubmitAnswer = () => { // 保留原来的坐标计算、setResponse逻辑即可,删掉后面的打印和判断部分 }
额外优化提示:你当前用eval获取box1到box4的ref的写法不推荐,建议把这四个ref统一存到数组里,直接遍历数组取值即可,避免eval带来的安全和性能问题。
内容的提问来源于stack exchange,提问作者user14399919
相关产品推荐
相关产品推荐

