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

如何在React Router v6中离开/关闭特定页面时发起请求?

解决React Router v6中页面卸载/导航时调用GraphQL Mutation的问题

核心问题分析

你遇到的问题本质是不同场景下的页面/组件销毁时机与异步请求的兼容性:

  • 应用内导航(含浏览器后退):触发React组件卸载,React的useEffect清理函数会执行,但常规异步请求可能被组件卸载中断
  • 页面卸载(关闭标签/浏览器、刷新):触发浏览器的beforeunload事件,但普通异步请求会被浏览器终止

可行解决方案:分场景覆盖 + 可靠请求方式

我们需要针对两种场景分别处理,同时使用浏览器提供的可靠API确保请求完成:

1. 封装通用的卸载请求函数

使用navigator.sendBeacon(优先)或fetch加keepalive参数,确保请求能在页面卸载时顺利完成:

const sendMutationOnUnload = async (mutationQuery, variables) => {
  // 构造GraphQL标准请求体
  const requestBody = JSON.stringify({
    query: mutationQuery,
    variables: variables
  });

  if (navigator.sendBeacon) {
    // sendBeacon 是浏览器专为卸载场景设计的API,后台执行不阻塞页面
    const blob = new Blob([requestBody], { type: 'application/json' });
    navigator.sendBeacon('/graphql', blob);
  } else {
    // 降级方案:使用fetch的keepalive参数确保请求完成
    await fetch('/graphql', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        // 若需认证,添加你的token头,例如:
        // 'Authorization': `Bearer ${localStorage.getItem('authToken')}`
      },
      body: requestBody,
      keepalive: true
    });
  }
};

2. 在目标页面组件中实现双场景监听

import { useEffect, useCallback } from 'react';
import { useBeforeUnload } from 'react-router-dom';

// 你的GraphQL Mutation定义
const MY_TARGET_MUTATION = `
  mutation RecordPageExit($pageId: String!) {
    recordPageExit(pageId: $pageId) {
      success
    }
  }
`;

const TargetPage = () => {
  // 替换为你的mutation变量
  const mutationVars = { pageId: 'user-dashboard' };

  // 处理【应用内导航/浏览器后退】:组件卸载时触发
  useEffect(() => {
    return async () => {
      await sendMutationOnUnload(MY_TARGET_MUTATION, mutationVars);
    };
  }, [mutationVars]);

  // 处理【关闭标签/浏览器/刷新】:页面卸载时触发
  const handleBeforeUnload = useCallback(() => {
    sendMutationOnUnload(MY_TARGET_MUTATION, mutationVars);
    // 若不需要提示用户,返回undefined即可;返回字符串会触发浏览器默认离开确认框
    return '确定要离开当前页面吗?';
  }, [mutationVars]);

  useBeforeUnload(handleBeforeUnload);

  return (
    <div>
      {/* 页面内容 */}
    </div>
  );
};

export default TargetPage;

为什么之前的方案失效?

  • useEffect清理函数:直接调用Apollo Client的异步mutation时,组件卸载会中断未完成的请求,改用sendBeacon/keepalive fetch可避免此问题
  • visibilitychange事件:仅监听页面可见性变化(最小化、切标签),与导航/卸载场景无关
  • beforeunload事件:之前的异步mutation被浏览器终止,sendBeacon是浏览器专为这类场景设计的API,会保证请求后台完成

注意事项

  • 认证处理:若API需要认证,确保请求携带正确的凭证(如token或cookie),cookie需满足SameSite和Secure要求
  • 浏览器兼容性:navigator.sendBeacon支持所有现代浏览器,降级方案可覆盖旧版浏览器
  • 自定义提示限制:浏览器会忽略beforeunload的自定义提示文字,统一显示系统默认提示,无法完全自定义

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 08:45:25