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

React 18与Next.js环境中在容器内使用createRoot触发警告的原因是什么?

React 18 + Next.js中使用createRoot挂载组件触发警告的原因

问题场景

我在React 18与Next.js环境中编写了如下渲染函数,用来挂载Alert组件到_document.tsx里的<div id="alert"></div>容器:

export const showAlert = (props: AlertProps) => { 
  const container = document.getElementById('alert') 
  if (container) { 
    const root = createRoot(container) 
    root.render(<Alert {...props} />) 
  } 
}

调用方式如下:

const handleClick = () => { 
  if (error) { 
    showAlert({ type: "error", text: "Error !" }) 
  } 
}

但React触发了警告,请问为什么会这样?


原因解析

1. 违反React的单根应用模型

Next.js框架会自动在主容器(通常是#__next)初始化一个React根节点,整个应用的组件树都挂载在这个根下,共享路由、全局状态、上下文等核心资源。

你手动创建的第二个根节点(挂载到#alert容器)是完全独立的,它无法访问主应用的上下文,React 18在开发模式下会对这种离散的根节点发出警告——因为这种模式容易导致状态不一致、上下文丢失,不符合React推荐的单根应用架构。

2. 重复创建根实例导致的资源泄漏

每次调用showAlert时,你都重新执行createRoot(container)创建新的根实例,然后调用render。React虽然会覆盖旧的组件树,但旧的根实例不会被正确清理,长期下来可能导致内存泄漏,这也是触发警告的另一个原因。


正确的解决方案:在主应用树中管理全局Alert

不要手动创建独立根节点,而是将Alert整合到主应用的组件树中,用状态管理控制显示/隐藏,示例如下:

第一步:创建Alert上下文与Provider

// app/AlertContext.tsx
import { createContext, useContext, useState, ReactNode } from 'react';

// 定义Alert的Props类型
type AlertProps = {
  type: "error" | "success" | "info";
  text: string;
};

type AlertContextType = {
  showAlert: (props: AlertProps) => void;
};

const AlertContext = createContext<AlertContextType | undefined>(undefined);

export const AlertProvider = ({ children }: { children: ReactNode }) => {
  const [alertProps, setAlertProps] = useState<AlertProps | null>(null);

  const showAlert = (props: AlertProps) => {
    setAlertProps(props);
    // 可选:3秒后自动关闭Alert
    setTimeout(() => setAlertProps(null), 3000);
  };

  return (
    <AlertContext.Provider value={{ showAlert }}>
      {children}
      {/* 全局Alert组件,根据状态显示/隐藏 */}
      {alertProps && <Alert {...alertProps} />}
    </AlertContext.Provider>
  );
};

// 自定义Hook,方便组件调用
export const useAlert = () => {
  const context = useContext(AlertContext);
  if (!context) {
    throw new Error('useAlert必须在AlertProvider内部使用');
  }
  return context;
};

第二步:在_app.tsx中挂载Provider

// app/_app.tsx
import { AlertProvider } from '@/app/AlertContext';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
  return (
    <AlertProvider>
      <Component {...pageProps} />
    </AlertProvider>
  );
}

第三步:在组件中调用showAlert

// 示例组件
import { useState } from 'react';
import { useAlert } from '@/app/AlertContext';

const DemoComponent = () => {
  const [error, setError] = useState(true);
  const { showAlert } = useAlert();

  const handleClick = () => { 
    if (error) { 
      showAlert({ type: "error", text: "Error !" }) 
    } 
  };

  return <button onClick={handleClick}>触发错误提示</button>;
};

export default DemoComponent;

这种方式完全遵循React的单根模型,Alert组件共享主应用的所有上下文,不会触发警告,同时也能更安全地管理组件的生命周期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 18:27:50