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

React+Socket.io页面刷新未等确认就断开连接问题求助

问题分析
  1. Socket提前断开并跳转的根源:你代码里监听socket.connected的useEffect会在页面刷新触发beforeunload时直接执行navigate("/")——因为浏览器触发beforeunload后会主动断开Socket连接,此时socket.connected变为false,导致用户还没确认是否离开就被强制跳转。
  2. beforeunload用法错误:beforeunload事件中不能自定义window.confirm对话框,浏览器会忽略这类自定义逻辑,必须通过event.returnValue触发默认确认提示。
  3. 路由拦截逻辑冲突:同时使用popstate监听和navigate的unblock方法,两者互相干扰,导致拦截逻辑失效。
解决方案

1. 移除Socket连接状态的自动跳转逻辑

先删掉导致提前跳转的代码,只在用户明确选择离开时处理Socket断开和跳转:

// 移除这段自动跳转的逻辑
// useEffect(() => {
//   if (!socket.connected) {
//     navigate("/");
//   }
// }, [socket, navigate]);

2. 正确实现beforeunload事件处理

beforeunload用于处理页面刷新、关闭标签页场景,浏览器会显示默认确认框,用户选择离开后页面才会卸载:

useEffect(() => {
  const handleBeforeUnload = (event) => {
    if (shouldPrompt) {
      event.preventDefault();
      // 兼容不同浏览器的提示设置(现代浏览器大多只显示默认文本)
      event.returnValue = "确定要离开当前页面吗?";
      return "确定要离开当前页面吗?";
    }
  };

  window.addEventListener("beforeunload", handleBeforeUnload);
  return () => window.removeEventListener("beforeunload", handleBeforeUnload);
}, [shouldPrompt]);

3. 用React Router官方API处理路由跳转拦截

放弃手动监听popstate,改用React Router提供的useBlocker处理路由跳转(后退/前进/主动跳转):
首先导入所需API:

import { useLocation, useNavigate, useBlocker } from "react-router-dom";

然后实现拦截逻辑:

const navigate = useNavigate();
const blocker = useBlocker();

useEffect(() => {
  const handleBlock = () => {
    if (shouldPrompt) {
      const shouldLeave = window.confirm("Are you sure you want to leave the page?");
      if (shouldLeave) {
        // 用户确认离开,处理Socket逻辑
        if (isHost && hasOpponent) {
          socket.emit("new_host", room);
        } else {
          socket.emit("leave_room", room);
        }
        blocker.proceed(); // 允许跳转
      } else {
        blocker.reset(); // 取消跳转
      }
    } else {
      blocker.proceed();
    }
  };

  blocker.on("block", handleBlock);
  return () => blocker.off("block", handleBlock);
}, [blocker, shouldPrompt, isHost, hasOpponent, room, socket]);

4. 统一管理shouldPrompt状态

确保只在需要拦截的场景下开启提示:

const [shouldPrompt, setShouldPrompt] = useState(false);

useEffect(() => {
  // 组件挂载完成后开启离开提示
  setShouldPrompt(true);
  // 组件卸载时关闭提示,避免影响其他页面
  return () => setShouldPrompt(false);
}, []);
核心代码片段整合
import React, { useState, useEffect } from "react";
import { useLocation, useNavigate, useBlocker } from "react-router-dom";

function Game() {
  const location = useLocation();
  const navigate = useNavigate();
  const blocker = useBlocker();
  const [shouldPrompt, setShouldPrompt] = useState(false);
  // ... 其他状态(isHost, hasOpponent, room, socket等)

  // 组件挂载后开启离开提示
  useEffect(() => {
    setShouldPrompt(true);
    return () => setShouldPrompt(false);
  }, []);

  // 处理页面刷新/关闭标签页
  useEffect(() => {
    const handleBeforeUnload = (event) => {
      if (shouldPrompt) {
        event.preventDefault();
        event.returnValue = "确定要离开当前页面吗?";
        return "确定要离开当前页面吗?";
      }
    };

    window.addEventListener("beforeunload", handleBeforeUnload);
    return () => window.removeEventListener("beforeunload", handleBeforeUnload);
  }, [shouldPrompt]);

  // 处理路由跳转拦截
  useEffect(() => {
    const handleBlock = () => {
      if (shouldPrompt) {
        const shouldLeave = window.confirm("Are you sure you want to leave the page?");
        if (shouldLeave) {
          if (isHost && hasOpponent) {
            socket.emit("new_host", room);
          } else {
            socket.emit("leave_room", room);
          }
          blocker.proceed();
        } else {
          blocker.reset();
        }
      } else {
        blocker.proceed();
      }
    };

    blocker.on("block", handleBlock);
    return () => blocker.off("block", handleBlock);
  }, [blocker, shouldPrompt, isHost, hasOpponent, room, socket]);

  // ... 组件剩余部分
}

export default Game;
关键注意点
  • beforeunload限制:现代浏览器不允许自定义beforeunload的确认框内容,只能显示浏览器默认提示,不要用window.confirm替代。
  • Socket处理时机:必须等用户明确确认离开后,再发送Socket断开事件,避免提前断开。
  • React Router拦截:使用官方useBlocker比手动监听popstate更可靠,不会和React Router内部逻辑冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 15:33:19