NextJS中useEffect依赖用window报错,如何监听导航类型变化?
问题描述
我需要实现一个函数,当window.performance.getEntriesByType("navigation")[0].type发生变化时执行。尝试用useEffect实现,但出现了“window is not defined”的引用错误,请问该如何实现函数在目标值变化时触发执行?
补充业务场景:玩家可创建房间,其他玩家通过房间ID加入;若房主离开页面则关闭房间,若其他玩家离开则将其移出房间。
相关代码:
useEffect(() => { interrupt() }, [window.performance.getEntriesByType("navigation")[0].type]); async function interrupt(){ if (players && currentRoom && window.performance.getEntriesByType("navigation")[0].type == 'reload') { const docRef = doc(db, "versus_ongoing", currentRoom) if(players.players[0].user == user.uid){ deleteDoc(docRef) } else { await updateDoc(docRef, { players: players.players.filter(post => post.host == true) }) } } else { console.info( "This page is not reloaded"); } }
解决方案
1. 先解决window is not defined错误
在React(尤其是SSR/SSG场景)中,window仅存在于浏览器环境,组件初始化阶段可能处于服务端,直接访问会报错。先做环境判断再获取导航类型:
const getNavigationType = () => { if (typeof window === 'undefined') return ''; const navigationEntry = window.performance.getEntriesByType("navigation")[0]; return navigationEntry?.type || ''; };
2. 调整监听逻辑,匹配业务需求
页面的导航类型在同一次会话中不会变化,你实际需要的是玩家离开页面时执行逻辑,用beforeunload事件更直接,同时修正useEffect的依赖问题:
import { useEffect, useState } from 'react'; // ... 其他业务代码 const [navigationType, setNavigationType] = useState(''); useEffect(() => { // 初始化时获取当前导航类型 setNavigationType(getNavigationType()); // 监听页面卸载/刷新事件,触发业务逻辑 const handlePageUnload = () => { interrupt(); }; window.addEventListener('beforeunload', handlePageUnload); // 组件卸载时移除监听 return () => { window.removeEventListener('beforeunload', handlePageUnload); }; }, [players, currentRoom, user.uid]); // 加入interrupt用到的所有外部依赖 async function interrupt() { if (!players || !currentRoom) return; const navType = getNavigationType(); if (navType === 'reload') { const docRef = doc(db, "versus_ongoing", currentRoom); if (players.players[0].user === user.uid) { await deleteDoc(docRef); } else { await updateDoc(docRef, { players: players.players.filter(post => post.host) }); } } else { console.info("This page is not reloaded"); } }
3. 关键说明
- 导航类型(
reload/navigate/back_forward)在页面加载完成后就固定,同会话内不会变化,所以“监听变化”的逻辑不成立,改用页面卸载事件更贴合业务场景。 useEffect的依赖数组必须包含interrupt用到的所有外部变量,避免闭包导致读取旧值的问题。- 加入环境判断后,无论是浏览器还是服务端渲染场景,都不会再出现
window is not defined错误。
内容的提问来源于stack exchange,提问作者Jaydendev
相关产品推荐
相关产品推荐

