React,socket.io页面刷新在没有确认提示选择的情况下继续。

huangapple go评论79阅读模式
英文:

React, socket.io page refresh continues without confirmation prompt selection

问题

我是一个初学者,我正在尝试在用户刷新页面时显示确认对话框,以便他们可以决定是否要离开页面。当用户点击浏览器刷新按钮时,确认框出现,但用户在做出选择之前会自动断开与套接字服务器的连接。

import React, { useState, useEffect, useCallback, useMemo } from "react";
// ...(其他导入和代码)

function Game() {
  // ...(之前的代码)
  const location = useLocation();
  const [shouldPrompt, setShouldPrompt] = useState(true);
  const [initialRender, setInitialRender] = useState(true);

  useEffect(() => {
    const unloadCallback = (event) => {
      event.preventDefault();
      event.returnValue = "";
      return "";
    };

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

  const handlePopstate = useCallback(
    (event) => {
      event.preventDefault();
      event.stopPropagation();

      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);
          }
          navigate("/");
        } else {
          window.history.pushState(null, null, window.location.pathname);
        }
      }
    },
    [hasOpponent, isHost, navigate, room, shouldPrompt, socket]
  );

  useEffect(() => {
    window.addEventListener("popstate", handlePopstate);

    return () => {
      window.removeEventListener("popstate", handlePopstate);
    };
  }, [handlePopstate]);

  useEffect(() => {
    if (!initialRender) {
      setShouldPrompt(true);
    } else {
      setInitialRender(false);
    }
  }, [initialRender, location.pathname]);

  useEffect(() => {
    if (shouldPrompt) {
      const unblock = navigate((tx) => {
        if (tx.action === "POP" && location.pathname === "/game") {
          return false;
        }
      });

      return unblock;
    }
  }, [navigate, shouldPrompt, location.pathname]);

  useEffect(() => {
    if (!socket.connected) {
      navigate("/");
    }
  }, [socket, navigate]);

  // 组件的其余部分...
}

export default Game;

我已经尝试了多种处理 "beforeunload" 的方式,但无论如何,刷新操作都会自动发生,尽管在出现确认提示时没有进行选择。

英文:

I am a beginner and I am trying to have a confirmation dialog box appear when a user refreshes a page so they can decide whether or not they want to leave the page. When a user clicks on the browser refresh, the confirmation box appears but the user disconnects automatically from socket server before making a selection.

import React, { useState, useEffect, useCallback, useMemo } from "react";
// ... (other imports and code)
function Game() {
// ... (previous code)
const location = useLocation();
const [shouldPrompt, setShouldPrompt] = useState(true);
const [initialRender, setInitialRender] = useState(true);
useEffect(() => {
const unloadCallback = (event) => {
event.preventDefault();
event.returnValue = "";
return "";
};
window.addEventListener("beforeunload", unloadCallback);
return () => window.removeEventListener("beforeunload", unloadCallback);
}, []);
const handlePopstate = useCallback(
(event) => {
event.preventDefault();
event.stopPropagation();
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);
}
navigate("/");
} else {
window.history.pushState(null, null, window.location.pathname);
}
}
},
[hasOpponent, isHost, navigate, room, shouldPrompt, socket]
);
useEffect(() => {
window.addEventListener("popstate", handlePopstate);
return () => {
window.removeEventListener("popstate", handlePopstate);
};
}, [handlePopstate]);
useEffect(() => {
if (!initialRender) {
setShouldPrompt(true);
} else {
setInitialRender(false);
}
}, [initialRender, location.pathname]);
useEffect(() => {
if (shouldPrompt) {
const unblock = navigate((tx) => {
if (tx.action === "POP" && location.pathname === "/game") {
return false;
}
});
return unblock;
}
}, [navigate, shouldPrompt, location.pathname]);
useEffect(() => {
if (!socket.connected) {
navigate("/");
}
}, [socket, navigate]);
// Rest of the component...
}
export default Game;

I've tried multiple variations of handling "beforeunload" and they all have the same result of the refresh action automatically happening despite no selection made on the confirmation prompt that appears.

答案1

得分: 1

问题很可能是beforeunload事件的异步触发,它有一些限制,并不能保证在代码执行之前页面不会刷新。

以您尝试的方式,beforeunload事件不会等待套接字断开的异步操作完成。

您可以通过阻止beforeunload上的默认行为,显示确认对话框,然后手动处理套接字断开和导航来解决此问题。

更新后的代码:

import React, { useState, useEffect, useCallback, useMemo } from "react";

function Game() {
  // ...(之前的代码)

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

  const handleBeforeUnload = useCallback((event) => {
    if (shouldPrompt) {
      event.preventDefault();
      event.returnValue = "确定要离开页面吗?";
    }
  }, [shouldPrompt]);

  const handleSocketDisconnect = useCallback(() => {
    if (shouldPrompt) {
      if (isHost && hasOpponent) {
        socket.emit("new_host", room);
      } else {
        socket.emit("leave_room", room);
      }
      setShouldPrompt(false); // 避免在套接字断开和页面导航后再次显示确认对话框。
      navigate("/");
    }
  }, [hasOpponent, isHost, navigate, room, shouldPrompt, socket]);

  useEffect(() => {
    window.addEventListener("beforeunload", handleBeforeUnload);
    socket.on("disconnect", handleSocketDisconnect);

    return () => {
      window.removeEventListener("beforeunload", handleBeforeUnload);
      socket.off("disconnect", handleSocketDisconnect);
    };
  }, [handleBeforeUnload, handleSocketDisconnect, socket]);

  // 组件的其余部分...
}

export default Game;

当用户刷新页面时,确认对话框应该出现,如果他们选择离开,将会相应地处理套接字断开和页面导航。shouldPrompt状态用于跟踪是否应提示用户,以避免在套接字断开和页面导航后再次显示确认对话框。

英文:

The problem is likely the async of the beforeunload event is triggered, it has some limitations and doesnt guarantee that the page wont refresh before the code executes.

with the way you are trying to do it, the beforeunload even doesn't wait for the async operations in the socket disconnection to complete.

You can fix this by preventing the default behavior on the beforeunload, show the confirmation dialog, and then handle the socket disconnection and navigation manually.

Updated Code:

import React, { useState, useEffect, useCallback, useMemo } from "react";
function Game() {
// ... (previous code)
const [shouldPrompt, setShouldPrompt] = useState(true);
const handleBeforeUnload = useCallback((event) => {
if (shouldPrompt) {
event.preventDefault();
event.returnValue = "Are you sure you want to leave the page?";
}
}, [shouldPrompt]);
const handleSocketDisconnect = useCallback(() => {
if (shouldPrompt) {
if (isHost && hasOpponent) {
socket.emit("new_host", room);
} else {
socket.emit("leave_room", room);
}
setShouldPrompt(false); // To avoid showing the confirmation dialog again after socket disconnection.
navigate("/");
}
}, [hasOpponent, isHost, navigate, room, shouldPrompt, socket]);
useEffect(() => {
window.addEventListener("beforeunload", handleBeforeUnload);
socket.on("disconnect", handleSocketDisconnect);
return () => {
window.removeEventListener("beforeunload", handleBeforeUnload);
socket.off("disconnect", handleSocketDisconnect);
};
}, [handleBeforeUnload, handleSocketDisconnect, socket]);
// Rest of the component...
}
export default Game;

when the user refreshes the page, the confirmation dialog should appear, and if they choose to leave, the socket disconnection and page navigation will be handled accordingly. The shouldPrompt state is used to track whether the user should be prompted or not to avoid showing the confirmation dialog again after the socket disconnection and page navigation.

huangapple
  • 本文由 发表于 2023年7月20日 12:39:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76726714.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定