告警 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 />);

以上全文。

相关推荐
hamburgerDaddy143 分钟前
从零开始用react + tailwindcs + express + mongodb实现一个聊天程序(二)
前端·javascript·mongodb·react.js·前端框架·express
bin91531 小时前
DeepSeek 助力 Vue 开发:打造丝滑的文本输入框(Text Input)
前端·javascript·vue.js·前端框架·ecmascript·deepseek
Alang3 小时前
Antd table 无限滚动+懒加载
前端·javascript·react.js
前端双越老师6 小时前
React19 和 Nextjs15 可否用于生产环境?
react.js·全栈·next.js
咔咔库奇6 小时前
【react】进阶教程02
前端·react.js·前端框架
七灵微7 小时前
【前端】react大全一本通
前端·react.js·前端框架
初遇你时动了情7 小时前
react使用react-quill 富文本插件、加入handlers富文本不显示解决办法
前端·javascript·react.js
野槐7 小时前
react路由总结
前端·javascript·react.js
青茶绿梅*216 小时前
500字理透react的hook闭包问题
javascript·react.js·ecmascript