React中如何用useEffect即时更新state?是否有其他可行替代方案?
问题原因
React的状态更新是异步批量执行的,调用setFilledBoxes后,当前函数作用域内的filledBoxes还是本次渲染时的旧值,所以直接打印会拿到上一轮的状态。另外Hook只能在组件顶层调用,不能嵌套在普通函数、条件判断、循环语句中,所以你把useEffect写在showSymbol里会触发Hook规则报错。
解决方法
方法1:使用useEffect监听状态变化(推荐)
把useEffect放在组件顶层,监听filledBoxes的变化,状态更新后useEffect的回调会自动触发,此时拿到的就是最新的状态值:
import { useState, useEffect } from 'react'; function Playfield() { const [ turn, setTurn ] = useState(0); const [ filledBoxes, setFilledBoxes ] = useState({ player1: [], player2: [] }); // 写在组件顶层,监听filledBoxes变化 useEffect(() => { console.log(filledBoxes.player1); console.log(filledBoxes.player2); // 所有需要在filledBoxes更新后执行的逻辑都可以放在这里 }, [filledBoxes]); function showSymbol (event) { if(event.target.innerHTML === "") { if(turn%2 === 0) { event.target.innerHTML = "X"; setFilledBoxes(prevValue => ({ ...prevValue, player1: [...prevValue.player1, event.target.id] })); } else { event.target.innerHTML = "O"; setFilledBoxes(prevValue => ({ ...prevValue, player2: [...prevValue.player2, event.target.id] })); } setTurn(turn + 1); } } // ... 其余组件代码 }
方法2:提前构造新状态值直接使用
如果你需要在当前函数执行流程中马上拿到新状态,不需要依赖React状态更新触发,可以先手动构造最新的状态对象,同时用它来更新状态和执行后续逻辑:
function showSymbol (event) { if(event.target.innerHTML === "") { // 先构造最新的filledBoxes对象 const newFilledBoxes = {...filledBoxes}; if(turn%2 === 0) { event.target.innerHTML = "X"; newFilledBoxes.player1 = [...newFilledBoxes.player1, event.target.id]; } else { event.target.innerHTML = "O"; newFilledBoxes.player2 = [...newFilledBoxes.player2, event.target.id]; } // 更新状态 setFilledBoxes(newFilledBoxes); // 直接使用构造好的新值即可,不需要等待状态更新 console.log(newFilledBoxes.player1); console.log(newFilledBoxes.player2); setTurn(turn + 1); } }
额外注意
尽量不要直接操作DOM修改innerHTML,React框架的核心是状态驱动渲染,建议把每个格子的内容存在状态中,通过状态渲染X/O,避免出现DOM和状态不一致的问题。
内容的提问来源于stack exchange,提问作者Shivam
相关产品推荐
相关产品推荐

