告警 Detected multiple renderers concurrently rendering the same context provider

使用 Arco Design 点击按钮弹框或者弹出Message提示框出现 Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported. 的警告,分析原因通常是因为在同一应用中存在多个 React 渲染器(React Renderer)并发渲染同一个 Context Provider。例如

  1. 多个 React 根节点 (React Root) 使用了相同的 Context Provider。
  2. 在测试环境或某些特殊场景下,同一个 Context 被多个独立的 React 实例渲染。
  3. 混合使用不同的 React 版本或渲染方式(如 ReactDOM 和 React Native)。

Arco Design 的某些组件(例如 Modal、Notification、Message 等)依赖全局 Context(如 ConfigProvider),当这些 Context 被多个渲染器同时操作时,就会触发此警告。


解决方法

1. 确保只有一个 React Root

检查你的应用是否创建了多个 React Root(通过 ReactDOM.rendercreateRoot)。如果有多个根节点,确保它们共享同一个 Context Provider。尤其是在使用了组件联邦的架构下,很容易出现此问题。

jsx 复制代码
// 不推荐:多个独立的 Root
ReactDOM.render(<App />, document.getElementById('root1'));
ReactDOM.render(<App />, document.getElementById('root2'));

// 推荐:只有一个 Root
ReactDOM.render(
  <ConfigProvider>
    <App />
  </ConfigProvider>,
  document.getElementById('root')
);

如果你使用 React 18 的 createRoot

jsx 复制代码
import { createRoot } from 'react-dom/client';
import { ConfigProvider } from '@arco-design/web-react';

const root = createRoot(document.getElementById('root'));
root.render(
  <ConfigProvider>
    <App />
  </ConfigProvider>
);

2. 检查 Arco 的全局组件使用方式

Arco Design 的 Modal、Notification 等组件会通过 Context 与全局状态交互。如果你手动调用这些组件的静态方法(例如 Modal.confirm),确保它们运行在同一个 React Context 树中。

错误示例:

jsx 复制代码
import { Modal } from '@arco-design/web-react';

// 在 React 树之外直接调用
Modal.confirm({
  title: '提示',
  content: '这是一个模态框',
});

正确示例: 确保 Modal 被包裹在 ConfigProvider 中:如果全局入口已经加了ConfigProvicer了还出现此警告,可以尝试在子组件继续包裹,如下

jsx 复制代码
import { ConfigProvider, Modal } from '@arco-design/web-react';

function App() {
  const showModal = () => {
    Modal.confirm({
      title: '提示',
      content: '这是一个模态框',
    });
  };

  return (
    <ConfigProvider>
      <button onClick={showModal}>打开 Modal</button>
    </ConfigProvider>
  );
}

3. 避免在测试环境中重复渲染 Context

在 Jest 或其他测试框架中,如果你在多个测试用例中重复渲染包含 ConfigProvider 的组件,可能会触发此警告。解决方法是确保每个测试用例独立清理渲染结果。

jsx 复制代码
import { render, screen } from '@testing-library/react';
import { ConfigProvider } from '@arco-design/web-react';

describe('MyComponent', () => {
  afterEach(() => {
    // 清理渲染
    document.body.innerHTML = '';
  });

  it('renders correctly', () => {
    render(
      <ConfigProvider>
        <MyComponent />
      </ConfigProvider>
    );
    expect(screen.getByText('test')).toBeInTheDocument();
  });
});

或者使用 @testing-library/reactcleanup 功能:

jsx 复制代码
import { render, screen, cleanup } from '@testing-library/react';
import { ConfigProvider } from '@arco-design/web-react';

afterEach(cleanup);

it('renders correctly', () => {
  render(
    <ConfigProvider>
      <MyComponent />
    </ConfigProvider>
  );
  expect(screen.getByText('test')).toBeInTheDocument();
});

4. 检查是否有第三方库导致的多重渲染

某些第三方库可能在独立的 React 实例中渲染组件,并与 Arco Design 的 Context 发生冲突。检查你的依赖中是否有类似情况,并尝试将这些库的渲染合并到主 React 树中。


排查步骤

  1. 定位问题来源 :在警告信息中查看完整的 Error Component Stack,找到触发警告的具体组件。
  2. 检查 Context 使用 :确认是否有多个 ConfigProvider 实例或独立渲染的 Arco 组件。
  3. 调试渲染树:使用 React Developer Tools 检查是否有多个独立的 React 树。
  4. 简化代码:逐步移除部分代码,定位触发警告的最小复现案例。

示例:完整修复后的代码

以下是一个典型的列表 + Modal 的实现:

jsx 复制代码
import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
import { ConfigProvider, Modal, Form, Input, Button, Table } from '@arco-design/web-react';

const App = () => {
  const [dataSource, setDataSource] = useState([]);
  const [isModalVisible, setIsModalVisible] = useState(false);
  const [editingItem, setEditingItem] = useState(null);
  const [form] = Form.useForm();

  const columns = [
    { title: '名称', dataIndex: 'name' },
    {
      title: '操作',
      render: (_, record) => (
        <Button onClick={() => {
          setEditingItem(record);
          form.setFieldsValue(record);
          setIsModalVisible(true);
        }}>编辑</Button>
      ),
    },
  ];

  const handleSubmit = (values) => {
    if (editingItem) {
      setDataSource(dataSource.map(item => 
        item.id === editingItem.id ? { ...item, ...values } : item
      ));
    } else {
      setDataSource([...dataSource, { id: Date.now(), ...values }]);
    }
    setIsModalVisible(false);
    form.resetFields();
    setEditingItem(null);
  };

  return (
    <ConfigProvider>
      <Button onClick={() => setIsModalVisible(true)} style={{ marginBottom: 16 }}>
        新增
      </Button>
      <Table dataSource={dataSource} columns={columns} rowKey="id" />
      <Modal
        title={editingItem ? '编辑' : '新增'}
        visible={isModalVisible}
        onCancel={() => {
          setIsModalVisible(false);
          form.resetFields();
          setEditingItem(null);
        }}
        footer={null}
      >
        <Form form={form} onFinish={handleSubmit} layout="vertical">
          <Form.Item name="name" label="名称" rules={[{ required: true }]}>
            <Input />
          </Form.Item>
          <Form.Item>
            <Button type="primary" htmlType="submit">
              提交
            </Button>
          </Form.Item>
        </Form>
      </Modal>
    </ConfigProvider>
  );
};

const root = createRoot(document.getElementById('root'));
root.render(<App />);

以上全文。

相关推荐
安分小尧3 小时前
[特殊字符] 使用 Handsontable 构建一个支持 Excel 公式计算的动态表格
前端·javascript·react.js·typescript·excel
ElasticPDF-新国产PDF编辑器4 小时前
React 项目 PDF 批注插件库在线版 API 示例教程
react.js·pdf·json
帅帅哥的兜兜5 小时前
react中hooks使用
前端·javascript·react.js
拉不动的猪14 小时前
刷刷题49(react中几个常见的性能优化问题)
前端·react.js·面试
小满zs16 小时前
React-router v7 第二章(路由模式)
前端·react.js
大莲芒16 小时前
react 15-16-17-18各版本的核心区别、底层原理及演进逻辑的深度解析--react18
前端·javascript·react.js
编程社区管理员18 小时前
「2025最新版React+Ant Design+Router+TailwindCss全栈攻略:从零到实战,打造高颜值企业级应用
前端·react.js·前端框架
gongzemin19 小时前
React 和 Vue3 在事件传递的区别
前端·vue.js·react.js
黄毛火烧雪下20 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js