Next.js全屏退出状态不同步问题:ESC按键需按两次如何解决?
问题解决思路
问题核心在于你仅通过监听ESC按键更新状态,但系统自带的全屏退出(包括ESC触发的场景)无法保证组件状态与实际全屏状态同步,同时缺少对全屏状态变化的通用监听,另外未清理事件监听还可能导致内存泄漏。
具体修复方案
- 监听
fullscreenchange事件:这是浏览器原生的全屏状态变化事件,无论通过ESC、浏览器按钮还是代码触发的全屏/退出操作,都会触发该事件,通过document.fullscreenElement可直接判断当前是否处于全屏状态。 - 清理事件监听:在
useEffect的返回函数中移除添加的事件监听,避免组件卸载后事件残留。 - 优化全屏触发逻辑:
requestFullscreen是异步操作,在其成功回调中更新状态,确保状态与实际全屏状态完全匹配。
修改后的完整代码
'use client' import { useState, useEffect } from 'react' const App = () => { const [isFScreen, setIsFScreen] = useState(false) useEffect(() => { // 同步全屏状态变化 const handleFullscreenChange = () => { setIsFScreen(!!document.fullscreenElement) } // 处理ESC按键(可选,fullscreenchange已覆盖状态同步,保留可主动触发退出) const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape" && document.fullscreenElement) { document.exitFullscreen() } } document.addEventListener("fullscreenchange", handleFullscreenChange) document.addEventListener("keydown", handleKeyDown) // 组件卸载时清理事件监听 return () => { document.removeEventListener("fullscreenchange", handleFullscreenChange) document.removeEventListener("keydown", handleKeyDown) } }, []) const toggleFullscreen = () => { const gameScreen = document.getElementById("gameScreen") if (!gameScreen) return if (!isFScreen) { gameScreen.requestFullscreen().then(() => { setIsFScreen(true) }).catch(err => { console.error("进入全屏失败:", err) }) } else { document.exitFullscreen().then(() => { setIsFScreen(false) }).catch(err => { console.error("退出全屏失败:", err) }) } } return( <div id="gameScreen"> <span onClick={toggleFullscreen}> {isFScreen ? '退出全屏' : '全屏'} </span> </div> ) } export default App
关键改动说明
fullscreenchange监听:确保任何场景下的全屏状态变化都能同步到组件状态,彻底解决ESC退出后状态不同步的问题。- 事件清理:避免组件重复渲染或卸载后残留事件监听,防止内存泄漏。
- 异步状态更新:利用全屏操作的Promise回调更新状态,保证状态与实际全屏状态完全一致。
- 动态按钮文本:根据当前状态切换按钮显示内容,提升用户体验。
内容的提问来源于stack exchange,提问作者co-flow
相关产品推荐
相关产品推荐

