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

React按钮数据加载后需双击才触发邮件发送问题排查

问题分析与解决方案

问题核心在于Recoil状态更新的异步性导致的时序问题:

  1. 点击按钮后调用setOrderID等方法更新Recoil状态,但状态更新不会立即生效,组件需要等到下一次渲染才能拿到新的状态值。
  2. 你在状态更新后立刻调用await fetchOrderData(),此时useEmailData中依赖的takeOrderIDState还是旧值,导致请求的是旧数据,emailData没有更新,第一次点击直接因!emailData返回。
  3. 第二次点击时,Recoil状态已完成更新,useEmailData拿到新ID,fetchOrderData才能获取到正确数据,邮件发送逻辑正常执行。

解决方案一:直接传递参数(推荐)

修改useEmailData,让fetchOrderData直接接收orderId和orderType参数,不再依赖Recoil状态,彻底避开异步状态更新的时序问题:

修改useEmailData:

const useEmailData = () => {
  // 直接接收参数的请求函数
  const fetchOrderDataFn = async (orderId, orderType) => {
    if (!orderId) return null;

    switch (orderType) {
      case "SOLO":
        return await auth.getSoloOrderId(orderId).then(res => res.data);
      case "DUO":
        return await auth.getDuoOrderId(orderId).then(res => res.data);
      case "NETWIN":
        return await auth.getNetWinsOrderId(orderId).then(res => res.data);
      case "COACH":
        return await auth.getCoachOrderId(orderId).then(res => res.data);
      default:
        return null;
    }
  };

  // 禁用自动执行,手动触发请求
  const {
    data: emailData,
    error: emailError,
    isLoading: dataLoading,
    refetch: refetchOrderData,
  } = useQuery({
    queryKey: ["emailData"],
    queryFn: () => null,
    enabled: false,
  });

  // 自定义fetch函数,传入参数并触发refetch
  const fetchOrderData = async (orderId, orderType) => {
    const result = await refetchOrderData({
      queryFn: () => fetchOrderDataFn(orderId, orderType),
      queryKey: ["emailData", orderId, orderType],
    });
    return result.data;
  };

  return {
    emailData,
    emailError,
    dataLoading,
    fetchOrderData,
  };
};

修改OrderCategoryList中的handleOrder:

const handleOrder = async (id, status, username, orderType, email) => {
  // 依然设置Recoil状态(如果其他组件需要使用)
  setOrderID(id);
  setOrderIDType(orderType);
  setOrdername(username);

  // 直接传入最新的id和orderType,获取数据
  const fetchedData = await fetchOrderData(id, orderType);
  if (!fetchedData) {
    console.log("Email data could not be loaded.");
    return;
  }

  if (BOOSTER_STATUS === "order") {
    alert("You cannot take another order.");
    return;
  }

  try {
    const templateParams = {
      // 直接使用fetchedData和传入的参数构建模板
      // 示例:orderData: fetchedData, userEmail: email, username: username
    };

    console.log('Template Params:', templateParams);
    await sendEmail(templateParams);
  } catch (error) {
    console.error('Failed to handle order:', error);
  }
};

解决方案二:监听Recoil状态变化触发流程

如果必须依赖Recoil状态,可以通过useEffect监听状态变化,在状态更新完成后自动触发数据获取和邮件发送:

修改OrderCategoryList:

const OrderCategoryList = () => {
  const { emailData, fetchOrderData, dataLoading } = useEmailData();
  const [orderData, setOrderData] = useState(null);
  const [userInfo, setUserInfo] = useRecoilState(userInfoAtom);
  const [orderID, setOrderID] = useRecoilState(takeOrderID);
  const setOrderIDType = useSetRecoilState(setOrderType);
  const setOrdername = useSetRecoilState(setOrderUsername);
  // 存储待发送邮件的必要信息
  const [pendingEmailTask, setPendingEmailTask] = useState(null);

  // 监听orderID和orderType变化,执行数据获取和邮件发送
  useEffect(() => {
    const processOrder = async () => {
      if (!orderID || !pendingEmailTask) return;

      await fetchOrderData();
      if (!emailData) {
        console.log("Email data could not be loaded.");
        setPendingEmailTask(null);
        return;
      }

      const { email, username } = pendingEmailTask;
      try {
        const templateParams = {
          // 使用emailData和待发送信息构建模板
        };
        await sendEmail(templateParams);
      } catch (error) {
        console.error('Failed to send email:', error);
      } finally {
        setPendingEmailTask(null);
      }
    };

    processOrder();
  }, [orderID, fetchOrderData, emailData, pendingEmailTask]);

  const handleOrder = (id, status, username, orderType, email) => {
    if (BOOSTER_STATUS === "order") {
      alert("You cannot take another order.");
      return;
    }

    // 设置Recoil状态
    setOrderID(id);
    setOrderIDType(orderType);
    setOrdername(username);
    // 标记待发送邮件任务
    setPendingEmailTask({ email, username });
  };

  const sendEmail = async (templateParams) => {
    // 邮件发送逻辑
  };

  // 其余渲染代码不变
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 09:50:11