React中如何在reducer内通过定时器更新状态?
你的问题核心有两个:一是在reducer里直接修改了原state对象,二是把setTimeout这种副作用放在了纯函数reducer里,而且定时器里直接改state不会触发组件重渲染。
React的reducer必须是纯函数:不能修改入参的state,所有状态更新都要返回新的对象;也不能包含异步操作、定时器这种副作用,这些逻辑应该放在组件或者自定义Hook里。
解决方案步骤
- 给reducer新增一个
HIDE_DOCS的action类型,专门用来隐藏提示框 - 把定时器逻辑从reducer移到
AppProvider的Getdocsaction中,通过dispatch触发状态更新 - 修正reducer里直接修改state的问题,所有状态更新都返回新对象
修改后的代码
reducer.js
const reducer = (state, action) => { switch(action.type) { case 'GET_DOCS': { const newPassports = state.oldDocs.filter(doc => doc.passport === true); if (newPassports.length === 0) { // 返回全新状态对象,不修改原state return { ...state, Passports: [], Showdocs: true, Getdocs: false }; } else { return { ...state, Passports: newPassports, Showdocs: true, Getdocs: !state.Getdocs }; } } case 'HIDE_DOCS': { return { ...state, Showdocs: false }; } default: throw new Error('no matching action type'); } }; export default reducer;
context.js
import React, { useContext, useReducer } from 'react' import reducer from './reducer' const AppContext = React.createContext() const initialState = { // ...你的其他状态 Showdocs: false, // ...你的其他状态 } const AppProvider = ({ children }) => { const [state, dispatch] = useReducer(reducer, initialState) const Getdocsaction = () => { dispatch({ type: 'GET_DOCS' }); // 在这里判断是否需要触发隐藏提示框的定时器 const newPassports = state.oldDocs.filter(doc => doc.passport === true); if (newPassports.length === 0) { setTimeout(() => { dispatch({ type: 'HIDE_DOCS' }); console.log("setTimeout fired"); }, 5000); } } return ( <AppContext.Provider value={{ ...state, Getdocsaction }} > {children} </AppContext.Provider> ) } export const useGlobalContext = () => { return useContext(AppContext) } export { AppContext, AppProvider }
关键提醒
React状态是不可变的,永远不要直接修改state对象(比如state.Showdocs = true这种写法),必须通过返回新对象或者dispatch action来更新,否则React检测不到状态变化,不会触发组件重渲染。
研究关键词
- React useReducer 纯函数规则
- React 副作用处理
- useContext + useReducer 异步状态更新
- React 不可变状态更新
内容的提问来源于stack exchange,提问作者ribosed
相关产品推荐
相关产品推荐

