React按钮数据加载后需双击才触发邮件发送问题排查
问题分析与解决方案
问题核心在于Recoil状态更新的异步性导致的时序问题:
- 点击按钮后调用
setOrderID等方法更新Recoil状态,但状态更新不会立即生效,组件需要等到下一次渲染才能拿到新的状态值。 - 你在状态更新后立刻调用
await fetchOrderData(),此时useEmailData中依赖的takeOrderIDState还是旧值,导致请求的是旧数据,emailData没有更新,第一次点击直接因!emailData返回。 - 第二次点击时,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
相关产品推荐
相关产品推荐

