React中跟踪Discord新窗口位置,遇错误URL自动关闭窗口
在React中跟踪新窗口位置并自动关闭指定URL窗口
问题原因
你之前用setInterval没生效,核心原因是跨域限制:当新窗口跳转到Discord的错误页面时,你的父页面(React应用)和新窗口属于不同域名,直接访问newWindow.location.href会触发浏览器的跨域安全报错,导致定时器内的代码中断执行,无法完成URL判断和窗口关闭操作。
解决方案
方案一:配置OAuth Redirect URI(推荐,可靠)
Discord OAuth支持通过redirect_uri参数指定授权完成后的跳转页面,你可以将跳转页设置为自己的前端页面,通过页面间通信实现窗口关闭:
- 修改窗口打开代码,添加
redirect_uri参数(需先在Discord开发者后台配置该地址为合法回调地址):
const newWindow = window.open( `https://discord.com/oauth2/authorize?client_id=${clientId}&scope=bot&permissions=962311146056&guild_id=${guildId}&redirect_uri=${encodeURIComponent('https://你的域名.com/oauth-callback')}`, "_blank", "width=900,height=800" );
- 创建OAuth回调页面(React组件),判断错误参数并通知父窗口:
// OAuthCallback.jsx import { useEffect } from 'react'; export default function OAuthCallback() { useEffect(() => { const searchParams = new URLSearchParams(window.location.search); const error = searchParams.get('error'); // 检测到授权拒绝错误,通知父窗口并关闭自身 if (error === 'access_denied') { window.opener.postMessage({ type: 'CLOSE_OAUTH_WINDOW' }, '*'); window.close(); } }, []); return <div>处理授权中...</div>; }
- 在父组件中监听消息事件,收到通知后关闭窗口:
import { useEffect, useState } from 'react'; export default function YourComponent() { const [newWindow, setNewWindow] = useState(null); const openAuthWindow = () => { const win = window.open( `https://discord.com/oauth2/authorize?client_id=${clientId}&scope=bot&permissions=962311146056&guild_id=${guildId}&redirect_uri=${encodeURIComponent('https://你的域名.com/oauth-callback')}`, "_blank", "width=900,height=800" ); setNewWindow(win); }; useEffect(() => { const handleMessage = (event) => { if (event.data.type === 'CLOSE_OAUTH_WINDOW' && newWindow) { newWindow.close(); } }; window.addEventListener('message', handleMessage); return () => { window.removeEventListener('message', handleMessage); }; }, [newWindow]); return <button onClick={openAuthWindow}>打开授权窗口</button>; }
方案二:跨域兼容的定时器监测(不推荐,不可靠)
如果无法配置回调地址,可通过捕获跨域错误的方式,尝试监测窗口状态:
import { useEffect } from 'react'; export default function YourComponent() { const errorUrl = "https://discord.com/oauth2/error?error=access_denied"; let intervalId; const openAuthWindow = () => { const newWindow = window.open( `https://discord.com/oauth2/authorize?client_id=${clientId}&scope=bot&permissions=962311146056&guild_id=${guildId}`, "_blank", "width=900,height=800" ); if (newWindow) { intervalId = setInterval(() => { try { // 尝试访问窗口URL,成功则判断是否为错误页面 if (newWindow.location.href === errorUrl) { newWindow.close(); clearInterval(intervalId); } } catch (e) { // 跨域错误时,检查窗口是否已关闭,关闭则清理定时器 if (newWindow.closed) { clearInterval(intervalId); } } }, 1000); // 窗口关闭时自动清理定时器 newWindow.addEventListener('beforeunload', () => { clearInterval(intervalId); }); } }; // 组件卸载时清理定时器 useEffect(() => { return () => { if (intervalId) clearInterval(intervalId); }; }, []); return <button onClick={openAuthWindow}>打开授权窗口</button>; }
注意:方案二的可靠性较低,因为浏览器跨域策略可能会限制对第三方窗口的任何访问,部分场景下可能无法正常触发判断。
内容的提问来源于stack exchange,提问作者d3m0l15h
相关产品推荐
相关产品推荐

