You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现含多异步函数的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核心要点

  1. 避免依赖异步更新的状态返回值:useState更新是异步的,不要在调用setXxx后立刻返回对应的状态,直接返回接口原始数据或让外部通过Hook暴露的状态获取最新值。
  2. 用useCallback稳定函数引用:异步函数会在组件每次渲染时重新创建,用useCallback包裹并声明正确依赖,避免触发不必要的useEffect重跑。
  3. 完善异步逻辑的边界处理:用try/catch/finally结构确保loading状态正确关闭,错误信息既要在Hook内部处理(如toast提示),也要抛出给调用方做额外处理。
  4. 不要忽略依赖项检查:禁用exhaustive-deps规则容易导致隐蔽bug,尽量补全所有依赖项。
  5. 命名简洁统一:Hook名称已明确作用,返回的状态/函数无需重复加Hook前缀(如useCartLoading改为loading)。

内容的提问来源于stack exchange,提问作者FiFi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 10:25:34