React Grid组件fetch数据后不重渲染、提前渲染的解决方案咨询
问题根因
- Reducer是纯函数,不允许包含异步/副作用逻辑:你在
readaction中调用异步的getRiders,dispatch执行时异步请求还未完成,直接返回了原始state,导致reducer状态没有触发更新 - 状态更新时机错误:Grid组件中dispatch后立刻读取localStorage,此时异步请求未结束,localStorage还未写入最新数据,
setRiders拿到的是旧值/空值;后续异步完成写入localStorage后,React默认不监听localStorage的变化,自然不会触发重渲染 - 冗余状态定义:同时使用
useReducer和useState存骑手数据,完全没有必要,useReducer返回的state本身就是响应式状态,可直接用于渲染
能不能用useContext解决?
useContext的作用是跨组件层级传递状态,解决的是状态共享的问题,不是异步更新、状态响应式的问题。你当前的问题核心是状态更新逻辑错误,哪怕把状态放到Context里也无法解决根因;如果后续需要多组件共享骑手状态,可以在修正状态逻辑的前提下,结合useContext做状态分发。
最优解决方案
核心思路是把异步逻辑从reducer中抽离,依赖React自身的响应式状态触发重渲染,localStorage仅作为持久化备份使用。
1. 修正ridersReducer,保持纯函数特性
import { updateRiders } from "../helpers/updateRiders"; export const ridersReducer = (state, action) => { switch (action.type){ case 'read': // 直接接收异步请求返回的新数据,返回新状态 return action.payload; case 'update': // 同步更新逻辑写在这里,接收payload返回新状态 return updateRiders(state, action.payload); default: return state; } };
2. 修正Grid组件逻辑,移除冗余状态,异步逻辑放在useEffect中
import { useEffect, useReducer, useState } from "react" import { ridersReducer } from "../hooks/ridersReducer"; import { GridRow } from "./GridRow"; import { getRiders } from "../helpers/getRiders"; export const Grid = ({ pressed, setPressed }) => { const [riders, dispatch] = useReducer(ridersReducer, []); // 可选:增加加载状态提升用户体验 const [loading, setLoading] = useState(false); useEffect(() => { const fetchRiders = async () => { setLoading(true); localStorage.clear(); try { const data = await getRiders(); // 处理数据逻辑 let newRiders; const localRiders = JSON.parse(localStorage.getItem('riders')); if (localRiders === null) { newRiders = data.record; } else { newRiders = [...localRiders, data.record]; } // 更新React状态,自动触发重渲染 dispatch({type: 'read', payload: newRiders}); // 持久化到localStorage localStorage.setItem('riders', JSON.stringify(newRiders)); } catch (err) { console.error('加载骑手数据失败', err); } finally { setLoading(false); } }; fetchRiders(); }, [pressed]); if (loading) return <div>加载中...</div>; return ( <table className="table table-hover"> <thead> <tr> <th scope="col">Horas</th> <th scope="col">Free Riders</th> <th scope="col">Estado</th> <th scope="col">Reservar</th> </tr> </thead> <tbody> {riders?.length > 0 && Object.keys(riders).map(rider => ( <GridRow id={riders[rider].id} key={riders[rider].id} hora={riders[rider].Hora} riders={riders[rider].Riders} pressed={pressed} setPressed={setPressed} dispatch={dispatch} /> ))} </tbody> </table> ) }
可选优化
如果需要实现多标签页之间的骑手数据同步,可以在组件中添加storage事件监听:
useEffect(() => { const handleStorageChange = (e) => { if (e.key === 'riders') { dispatch({type: 'read', payload: JSON.parse(e.newValue)}); } }; window.addEventListener('storage', handleStorageChange); return () => window.removeEventListener('storage', handleStorageChange); }, []);
内容的提问来源于stack exchange,提问作者Skelett7
相关产品推荐
相关产品推荐

