使用axios+useReducer/Context/Provider时,todo无法获取initialState值的问题
问题原因与解决方案
对,你猜的没错——useReducer仅在组件首次渲染时读取第二个参数作为初始状态。后续setInitialState更新initialState导致组件重渲染,但useReducer不会用新的initialState去更新todo状态,所以todo始终停留在初始的空数组。
方式一:直接在请求成功后dispatch初始化action(推荐)
去掉中间的initialState状态,直接把请求到的数据通过dispatch传给reducer,逻辑更简洁,减少状态冗余:
export function TodoProvider({children}){ const apiUrl = "http://localhost:3001/home"; // 初始化todo为空数组 const [todo, dispatch] = useReducer(todoReducer, []); useEffect(()=>{ axios.get(apiUrl) .then((response)=>{ // 向reducer发送初始化action dispatch({ type: 'INIT_TODOS', payload: response.data.data }); }) .catch(e=>console.log(e)) },[]); return ( <TodoContext.Provider value={todo}> <TodoDispatchContext.Provider value={dispatch}> {children} </TodoDispatchContext.Provider> </TodoContext.Provider> ) }
对应的reducer需要处理INIT_TODOS动作:
function todoReducer(state, action) { switch(action.type) { case 'INIT_TODOS': // 直接将状态替换为请求到的todo数据 return action.payload; // 其他动作处理(比如新增、删除todo) default: return state; } }
方式二:监听initialState变化,触发dispatch更新
如果需要保留initialState状态,可以通过额外的useEffect监听它的变化,当数据加载完成后,再dispatch更新todo:
export function TodoProvider({children}){ const apiUrl = "http://localhost:3001/home"; const [initialState,setInitialState] = useState([]); const [todo, dispatch] = useReducer(todoReducer, initialState); useEffect(()=>{ axios.get(apiUrl) .then((response)=>{ setInitialState(response.data.data); }) .catch(e=>console.log(e)) },[]); // 当initialState有数据时,更新todo状态 useEffect(() => { // 避免初始空数组时触发 if (initialState.length > 0) { dispatch({ type: 'INIT_TODOS', payload: initialState }); } }, [initialState]); return ( <TodoContext.Provider value={todo}> <TodoDispatchContext.Provider value={dispatch}> {children} </TodoDispatchContext.Provider> </TodoContext.Provider> ) }
关键提醒
React的useReducer和useState一样,初始值仅在组件第一次渲染时生效。后续状态的更新必须通过dispatch(useReducer)或setXxx(useState)来触发,不能依赖初始值变量的更新。
内容的提问来源于stack exchange,提问作者lycheelichi
相关产品推荐
相关产品推荐

