React useEffect重置Context筛选值过晚致请求参数异常
问题背景
当前实现逻辑如下:
- 在
filtersContext.js中完成FilterContextProvider与自定义HookuseFilters的开发:- 基于
React.createContext创建上下文 - Provider组件内部通过
useState维护全局filters状态 useFiltersHook通过useContext读取上下文,同时在useEffect的卸载清理函数中调用setFilters({}),期望组件卸载时自动重置筛选值
- 基于
- 根组件
App.js中全局包裹FiltersProvider,所有路由下的页面组件都可以访问该筛选上下文
异常表现
在Page1页面选中筛选条件后跳转至Page2时,原本期望useFilters的卸载逻辑自动清空filters,实际执行时序不符合预期:
- Page2首次渲染时读取到的是Page1遗留的旧filters值,直接触发携带旧筛选参数的GraphQL
useQuery请求 - 旧值请求发出后,Page1组件对应的
useFiltersHook才执行卸载清理逻辑,将filters重置为空对象,触发Page2二次渲染
控制台日志可印证该执行顺序:先打印Page2首次渲染日志、旧filters值,再打印filters重置日志,最后打印空filters值与Page2二次渲染日志。
根本原因:React生命周期执行顺序决定了旧组件的useEffect卸载清理函数,晚于新组件的首次渲染执行,把筛选重置逻辑放在旧组件的卸载回调里,必然会出现新组件先拿到旧状态的时序问题,和Context本身实现无关。
解决方案
方案1:路由跳转前主动重置状态(优先级最高,无副作用)
完全移除useFilters中写在useEffect卸载回调里的重置逻辑,在Page1触发路由跳转的事件中,先执行筛选状态重置,再做路由跳转,从源头规避时序问题。
代码示例:
// Page1 页面内跳转逻辑 const navigate = useNavigate(); const { setFilters } = useFilters(); const handleJumpToPage2 = () => { setFilters({}); // 跳转前同步清空筛选状态 navigate('/page2'); }
该方案下路由跳转完成后Page2首次渲染拿到的就是空筛选值,不会触发携带旧参数的无效请求。
方案2:给筛选值增加页面归属标识
如果筛选值本身是页面级状态,不建议放在全局Context中存储,可直接下沉到对应页面做局部状态管理;如果确实需要全局共享跨组件的筛选状态,可在Context中增加当前激活页面标识,各页面只读取属于自己的筛选值。
代码示例:
// useFilters 改造 const useFilters = (currentPageKey) => { const context = useContext(FilterContext); // 非当前页面的筛选值直接返回空,避免跨页面污染 return context.activePage === currentPageKey ? context.filters : {}; } // 路由跳转时同步更新activePage标识 const handleJumpToPage2 = () => { setActivePage('page2'); navigate('/page2'); }
方案3:请求层做参数校验兜底
在Page2的GraphQL useQuery逻辑中,不显式透传整个filters对象,只提取当前页面需要的筛选字段,不存在对应字段时默认传空值,忽略其他页面遗留的筛选参数。
代码示例:
// Page2 页面请求逻辑 const { filters } = useFilters(); const queryVariables = useMemo(() => ({ // 仅拼接Page2自身需要的筛选参数 status: filters.page2Status || undefined, keyword: filters.page2Keyword || '' }), [filters]); useQuery(GET_PAGE2_LIST, { variables: queryVariables });
该方案作为兜底逻辑,即使状态更新存在延迟,也不会把其他页面的筛选参数带到当前页面的请求中。
内容的提问来源于stack exchange,提问作者6axter82
相关产品推荐
相关产品推荐

