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

以上全文。

相关推荐
哈哈你是真的厉害23 分钟前
小白基础入门 React Native 鸿蒙跨平台开发:AnimatedSpring 弹簧动画
react native·react.js·harmonyos
哈哈你是真的厉害33 分钟前
基础入门 React Native 鸿蒙跨平台开发:颜色选择器工具
react native·react.js·harmonyos
哈哈你是真的厉害2 小时前
基础入门 React Native 鸿蒙跨平台开发:实现一个简单的倒计时工具
react native·react.js·harmonyos
ghgxm5203 小时前
Fastapi_00_学习策略与学习计划
python·学习·前端框架·npm·fastapi
哈哈你是真的厉害3 小时前
基础入门 React Native 鸿蒙跨平台开发:实现AnimatedValueXY 双轴动画
react native·react.js·harmonyos
放逐者-保持本心,方可放逐3 小时前
React核心组件 及 钩子函数应用
前端·javascript·react.js·非阻塞更新·延迟更新·同步更新
斯普信专业组4 小时前
ReAct Agent 解析:从理论到实践的高效推理框架
前端·react.js·前端框架
程序员张34 小时前
Element Plus SCSS 变量覆盖用法
vue.js·前端框架·element-plus
敲敲了个代码4 小时前
React 官方纪录片观后:核心原理解析与来龙去脉
前端·javascript·react.js·面试·架构·前端框架
摘星编程4 小时前
React Native鸿蒙:LayoutAnimation配置弹簧动画
react native·react.js·harmonyos