You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将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;

关键说明

  1. Hooks上下文合规:现在ToastComponent是标准React函数组件,useState在组件渲染阶段调用,完全符合React Hooks的调用规则。
  2. 延迟渲染:组件的JSX生成和渲染操作延迟到render方法执行时,此时容器已被添加到DOM,React可以正确建立组件上下文。
  3. 自动清理:通过onDestroy回调,在Snackbar关闭后自动移除DOM容器,避免内存泄漏和无用DOM节点堆积。

内容的提问来源于stack exchange,提问作者liuyulvv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 05:17:10