将React Toast组件挂载到window时Hook调用报错的解决方法
问题解决:保留函数组件与Hooks前提下实现全局Toast
错误原因
你当前的代码在Toast类的构造函数中直接调用了ToastComponent,此时useState是在普通JavaScript类的构造函数环境中执行,而非React函数组件的渲染上下文,违反了React Hooks只能在函数组件或自定义Hooks内部调用的规则,因此触发Invalid hook call错误。
解决方案
核心调整点
- 将
ToastComponent改为标准React函数组件,通过props接收参数 - 不在构造函数中提前生成JSX元素,延迟到
render方法中再渲染组件 - 添加组件关闭后的自动清理逻辑,避免DOM冗余
修改后的完整代码
Toast组件代码
import { Alert, Snackbar } from '@mui/material'; import { useState } from 'react'; import { createRoot } from 'react-dom/client'; // 改为标准React函数组件,通过props接收参数和销毁回调 const ToastComponent = ({ message, type, onDestroy }: { message: string; type: 'info' | 'success' | 'warning' | 'error'; onDestroy: () => void; }) => { const [open, setOpen] = useState(true); const handleClose = (event?: React.SyntheticEvent | Event, reason?: string) => { setOpen(false); // Snackbar关闭后触发销毁逻辑 setTimeout(onDestroy, 0); }; return ( <Snackbar open={open} autoHideDuration={5000} anchorOrigin={{ vertical: 'top', horizontal: 'center' }} onClose={handleClose} > <Alert severity={type} sx={{ width: '100%' }}> {message} </Alert> </Snackbar> ); }; export default class Toast { public container: HTMLElement; private message: string; private type: 'info' | 'success' | 'warning' | 'error'; public constructor(message: string, type: 'info' | 'success' | 'warning' | 'error') { this.container = document.createElement('div'); // 仅保存参数,不提前渲染组件 this.message = message; this.type = type; } public destroy() { const rootContainer = document.getElementById('root') as HTMLDivElement; // 确保容器存在于父节点中再移除 if (this.container.parentNode === rootContainer) { rootContainer.removeChild(this.container); } } public render() { const rootContainer = document.getElementById('root') as HTMLDivElement; rootContainer.appendChild(this.container); const root = createRoot(this.container); // 在render方法中才渲染组件,确保Hooks运行在React上下文 root.render( <ToastComponent message={this.message} type={this.type} onDestroy={() => this.destroy()} /> ); } }
index.ts代码(保持不变)
import Toast from './Toast'; // eslint-disable-next-line @typescript-eslint/no-explicit-any (<any>window).Toast = Toast;
关键说明
- Hooks上下文合规:现在
ToastComponent是标准React函数组件,useState在组件渲染阶段调用,完全符合React Hooks的调用规则。 - 延迟渲染:组件的JSX生成和渲染操作延迟到
render方法执行时,此时容器已被添加到DOM,React可以正确建立组件上下文。 - 自动清理:通过
onDestroy回调,在Snackbar关闭后自动移除DOM容器,避免内存泄漏和无用DOM节点堆积。
内容的提问来源于stack exchange,提问作者liuyulvv
相关产品推荐
相关产品推荐

