如何实现含多异步函数的React自定义Hook?解决旧数据返回问题
问题原因分析
你的useCart Hook里的getCartList函数存在逻辑错误:React的useState更新是异步的,调用setCart(res.data)后立即return cart,此时cart还是更新前的旧状态值,这就是首次调用返回旧数据的根本原因。而直接写的viewCartList能正常工作,是因为它直接返回了接口的原始响应数据,没有依赖异步更新的状态。
修复后的useCart Hook实现
import { useState, useEffect, useCallback } from 'react'; const useCart = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [cart, setCart] = useState([]); const toast = useToast(); // 用useCallback包裹异步函数,确保引用稳定 const addToCart = useCallback(async (categoryId, quantity) => { setLoading(true); try { const res = await ax.post("/cart", { category: categoryId, quantity }); setCart(res.data?.categories); } catch (err) { console.log(err); setError(err.response?.data); } finally { // 用finally确保无论请求成功/失败,都关闭loading setLoading(false); } }, [ax]); const getCartList = useCallback(async () => { setLoading(true); try { const res = await ax.get("/cart/"); setCart(res.data); // 直接返回接口响应的最新数据,而非依赖异步更新的cart状态 return res.data; } catch (err) { console.log(err); const errorMsg = err.response?.data; setError(errorMsg); // 抛出错误让调用方可以捕获处理 throw new Error(errorMsg); } finally { setLoading(false); } }, [ax]); const emptyCartState = useCallback(() => { setCart([]); }, []); // 补全依赖项,移除eslint禁用规则 useEffect(() => { if (error) { toast({ title: error, status: "error", isClosable: true, }); // 显示toast后清空错误,避免重复触发提示 setError(null); } }, [error, toast]); return { addToCart, cart, loading, // 简化命名,Hook前缀已说明作用 error, getCartList, emptyCartState, }; }; export default useCart;
修复后的调用代码
const { user } = useContext(AuthContext); const { cartDispatch } = useContext(CartContext); const { cart, getCartList, loading } = useCart(); useEffect(() => { const updateCart = async () => { if (!user) { cartDispatch({ type: "UPDATE_CART", payload: [] }); return; } try { // 直接获取getCartList返回的最新接口数据 const updatedCart = await getCartList(); cartDispatch({ type: "UPDATE_CART", payload: updatedCart }); } catch (err) { console.error('获取购物车数据失败:', err); // 此处可添加调用方的错误处理逻辑 } }; updateCart(); }, [user, getCartList, cartDispatch]); // 补全所有依赖项
编写含多异步函数的自定义Hook核心要点
- 避免依赖异步更新的状态返回值:
useState更新是异步的,不要在调用setXxx后立刻返回对应的状态,直接返回接口原始数据或让外部通过Hook暴露的状态获取最新值。 - 用
useCallback稳定函数引用:异步函数会在组件每次渲染时重新创建,用useCallback包裹并声明正确依赖,避免触发不必要的useEffect重跑。 - 完善异步逻辑的边界处理:用
try/catch/finally结构确保loading状态正确关闭,错误信息既要在Hook内部处理(如toast提示),也要抛出给调用方做额外处理。 - 不要忽略依赖项检查:禁用
exhaustive-deps规则容易导致隐蔽bug,尽量补全所有依赖项。 - 命名简洁统一:Hook名称已明确作用,返回的状态/函数无需重复加Hook前缀(如
useCartLoading改为loading)。
内容的提问来源于stack exchange,提问作者FiFi
相关产品推荐
相关产品推荐

