ReactJS与NextJS集成ADA Chatbot页面切换初始化问题
解决Next.js/React集成ADA聊天机器人页面切换时窗口关闭的问题
问题背景
在React/Next.js项目中集成ADA聊天机器人,要求仅在A、B页面显示聊天气泡,C页面不加载。但从A页面切换到B页面时,聊天窗口会意外关闭。通过日志发现,B页面的adaEmbed.start()执行完成的时间早于A页面的adaEmbed.stop(),导致A页面的stop操作会关闭已经启动的B页面聊天窗口。
现有代码问题
当前Chat组件在挂载时启动聊天,卸载时直接停止聊天。页面切换时,A组件的卸载逻辑(stop)是异步操作,而B组件的启动逻辑先完成,后续A的stop执行会关闭已启动的聊天窗口。
import { FC, useEffect } from 'react'; import adaEmbed from '@ada-support/embed2'; const Chat: FC<{ caller: string }> = ({ caller }) => { useEffect(() => { console.log('mounting:', caller); adaEmbed .start({ handle: 'HANDLE', }) .then(() => { console.log('started:', caller); }) return () => { console.log('unmounting:', caller); adaEmbed .stop() .then(() => { console.log('stopped', caller); }) }; }, [caller]); return null; }; export default Chat;
解决方案
方案1:全局计数器跟踪活跃实例
通过全局计数器跟踪需要显示聊天的页面数量,仅当计数器归0(即所有A/B页面都已卸载,切换到C页面)时才执行stop操作。
修改Chat组件如下:
import { FC, useEffect, useRef } from 'react'; import adaEmbed from '@ada-support/embed2'; // 全局计数器:跟踪当前需要显示聊天的页面实例数 let chatActiveInstances = 0; const Chat: FC = () => { const isChatInitialized = useRef(false); useEffect(() => { chatActiveInstances++; // 仅第一次启动聊天,避免重复初始化 if (!isChatInitialized.current) { adaEmbed.start({ handle: 'HANDLE' }) .then(() => { isChatInitialized.current = true; console.log('Chat initialized'); }); } return () => { chatActiveInstances--; // 只有当没有活跃的聊天页面时,才停止聊天 if (chatActiveInstances === 0) { adaEmbed.stop() .then(() => { isChatInitialized.current = false; console.log('Chat stopped'); }); } }; }, []); return null; }; export default Chat;
使用方式:
- 在A、B页面中通过Next.js动态导入并渲染该组件(保持
ssr: false确保客户端渲染) - C页面不渲染该组件
原理:A→B切换时,A卸载使计数器从1变为0,但同时B挂载使计数器回到1,因此不会触发stop;切换到C时,计数器归0,执行stop关闭聊天窗口。
方案2:路由全局监听控制启停
在Next.js的_app.tsx中监听路由变化,仅当离开A/B页面时执行stop,A/B之间切换不操作。
import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; import adaEmbed from '@ada-support/embed2'; function MyApp({ Component, pageProps }) { const router = useRouter(); const [isChatActive, setIsChatActive] = useState(false); useEffect(() => { // 判断当前页面是否需要显示聊天 const shouldActivateChat = () => ['/a', '/b'].includes(router.pathname); // 初始化时检查状态 const initialState = shouldActivateChat(); setIsChatActive(initialState); if (initialState) adaEmbed.start({ handle: 'HANDLE' }); // 路由切换完成后更新状态 const handleRouteChange = () => { const newState = shouldActivateChat(); if (newState && !isChatActive) { adaEmbed.start({ handle: 'HANDLE' }); } else if (!newState && isChatActive) { adaEmbed.stop(); } setIsChatActive(newState); }; router.events.on('routeChangeComplete', handleRouteChange); // 页面卸载时清理 return () => { router.events.off('routeChangeComplete', handleRouteChange); if (isChatActive) adaEmbed.stop(); }; }, [router, isChatActive]); return <Component {...pageProps} />; } export default MyApp;
原理:直接在全局路由层面控制聊天的启停,A/B之间切换时状态保持为true,不会执行stop操作,避免窗口关闭。
注意事项
- 确保Chat组件通过Next.js动态导入并设置
ssr: false,避免服务端渲染报错:import dynamic from 'next/dynamic'; const Chat = dynamic(() => import('./Chat'), { ssr: false });
内容的提问来源于stack exchange,提问作者Babu
相关产品推荐
相关产品推荐

