ReactJS使用useState重渲染组件 Axios请求后状态更新异常
问题根源
你的代码无法触发重渲染是几个明确的逻辑错误导致的:
setSlotsList(...slotsList)写法本身错误:slotsList是数组类型状态,展开后传入setState的是零散的数组元素,不是合法的数组状态值,React无法识别有效状态更新。- 状态更新时机错误:Axios请求是异步操作,你在发起请求的同步代码块里就调用setState,没有等待接口返回成功,此时数据库更新还未完成,本地也拿不到最新的slot数据。
- 状态更新未生成新引用:React对对象、数组类型的状态做浅比较判断是否更新,如果你传入的状态和原状态是同一个引用,哪怕内容变了也不会触发重渲染。
- 现有useEffect依赖数组为空,只会在组件首次挂载时拉取一次列表,后续预约操作完成后不会自动触发重新拉取。
修复方案
根据你的业务场景二选一即可:
方案1:预约成功后重拉全量列表(数据一致性最高)
这是最稳妥的实现,完全以服务端返回的最新数据为准,避免本地状态和数据库不一致的问题。先把拉取slot列表的逻辑抽成可复用函数,在预约接口返回成功后调用即可:
const [slotsList, setSlotsList] = useState([]); // 抽离获取最新时段列表的逻辑 const getSlotsList = () => { Axios.post("http://localhost:3001/api/get/week1/ex").then((response) => { setSlotsList(response.data); }); }; // 组件首次挂载时拉取一次列表 useEffect(() => { getSlotsList(); }, []); const canBookSlot = (id) => { if (userDetailsObj.canBook != 0) { Axios.post("http://localhost:3001/api/book/week1/ex", { room: userDetailsObj.room, id: id.id + 1, }).then(() => { // 预约接口返回成功后,重新拉取最新列表触发重渲染 getSlotsList(); console.log("booked"); }); } };
方案2:本地手动更新状态(无额外请求,响应更快)
如果不想多发一次列表请求,可以在预约成功后手动修改本地状态,重点是必须返回一个全新的数组引用,绝对不能直接修改原数组:
const canBookSlot = (id) => { if (userDetailsObj.canBook != 0) { const bookId = id.id + 1; Axios.post("http://localhost:3001/api/book/week1/ex", { room: userDetailsObj.room, id: bookId, }).then(() => { setSlotsList(prevList => { // 基于原状态生成全新数组,不要修改prevList里的元素 return prevList.map(slot => { if(slot.id === bookId) { // 替换成你实际需要更新的字段,比如标记为已预约 return { ...slot, isBooked: true } } return slot; }) }); console.log("booked"); }); } };
避坑提醒:不要写出
setSlotsList(slotsList)这种直接传原数组引用的代码,React会判定状态无变化跳过渲染,所有数组/对象类型的状态更新都要返回新的引用。
内容的提问来源于stack exchange,提问作者Max Andersson
相关产品推荐
相关产品推荐

