如何在useEffect中实现清理函数优化Context的API数据获取
解决切换类别时旧产品数据残留的问题
核心问题分析
你的filter_products是全局上下文状态,切换类别时新API请求还未返回,组件会先渲染之前的旧数据;而且原清理逻辑仅标记inView,既没真正清空状态,也没处理API请求的竞态问题——旧请求可能晚于新请求完成,导致新数据被覆盖。
具体修复步骤
1. 给FilterReducer添加清空数据的动作
在你的reducer函数里新增CLEAR_FILTER_PRODUCTS类型的处理:
const reducer = (state, action) => { switch(action.type) { // 保留现有其他case逻辑 case "CLEAR_FILTER_PRODUCTS": return { ...state, filter_products: [], // 可选:如果有loading/error状态,也可一并重置 // isLoading: false, // error: "" }; default: return state; } };
2. 在FilterProvider中添加清空数据的方法
修改FilterProvider,新增clearFilterProducts方法并传入上下文:
const FilterProvider = ({ children }) => { const [state, dispatch] = useReducer(reducer, initialState); const { products } = useAppContext(); const getFilterProducts = async (url) => { dispatch({ type: "FILTERS_LOADING" }); try { const res = await fetch(url); const data = await res.json(); if (!res.ok) { var error = new Error("Error" + res.status + res.statusText); throw error; } dispatch({ type: "LOAD_FILTER_PRODUCTS", payload: data.products }); } catch (err) { dispatch({ type: "FILTERS_ERROR", payload: err.message }); } }; // 新增清空数据的方法 const clearFilterProducts = () => { dispatch({ type: "CLEAR_FILTER_PRODUCTS" }); }; return ( <FilterContext.Provider value={{ ...state, getFilterProducts, clearFilterProducts }}> {children} </FilterContext.Provider> ); };
3. 在ProductsList组件中卸载时清空数据
修改ProductsList的useEffect,在清理函数里调用clearFilterProducts:
const ProductsList = () => { const { cate } = useParams(); const { getFilterProducts, filter_products, clearFilterProducts } = useFilterContext(); useEffect(() => { getFilterProducts(`${API}/category/${cate}`); // 组件卸载时清空旧数据 return () => { clearFilterProducts(); }; }, [cate, getFilterProducts, clearFilterProducts]); return ( <div className="mx-2 mt-2 mb-16 md:mb-0 grid grid-cols-1 md:grid-cols-12"> <div className="h-9 w-full md:col-span-2"> <FilterSection /> </div> <div className="md:col-span-10"> <ProductListDetails products={filter_products} /> </div> </div> ); };
4. 可选:处理API请求竞态问题
如果快速切换类别时出现旧请求覆盖新数据的情况,可用AbortController取消旧请求:
// 修改FilterProvider中的getFilterProducts方法 const getFilterProducts = async (url, signal) => { dispatch({ type: "FILTERS_LOADING" }); try { const res = await fetch(url, { signal }); // 传入取消信号 const data = await res.json(); if (!res.ok) { var error = new Error("Error" + res.status + res.statusText); throw error; } dispatch({ type: "LOAD_FILTER_PRODUCTS", payload: data.products }); } catch (err) { // 忽略请求被取消的错误 if (err.name !== "AbortError") { dispatch({ type: "FILTERS_ERROR", payload: err.message }); } } };
然后更新ProductsList的useEffect:
useEffect(() => { const controller = new AbortController(); const { signal } = controller; getFilterProducts(`${API}/category/${cate}`, signal); return () => { controller.abort(); // 取消当前未完成的请求 clearFilterProducts(); }; }, [cate, getFilterProducts, clearFilterProducts]);
修改完成后,切换类别时组件卸载会清空旧数据,再次进入时会先显示空状态(可配合loading状态添加加载提示),等API请求返回后展示当前类别的数据,彻底解决旧数据残留的问题。
内容的提问来源于stack exchange,提问作者Prince Singh
相关产品推荐
相关产品推荐

