使用 Arco Design 点击按钮弹框或者弹出Message提示框出现 Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.
的警告,分析原因通常是因为在同一应用中存在多个 React 渲染器(React Renderer)并发渲染同一个 Context Provider。例如
- 多个 React 根节点 (React Root) 使用了相同的 Context Provider。
- 在测试环境或某些特殊场景下,同一个 Context 被多个独立的 React 实例渲染。
- 混合使用不同的 React 版本或渲染方式(如 ReactDOM 和 React Native)。
Arco Design 的某些组件(例如 Modal、Notification、Message 等)依赖全局 Context(如 ConfigProvider),当这些 Context 被多个渲染器同时操作时,就会触发此警告。
解决方法
1. 确保只有一个 React Root
检查你的应用是否创建了多个 React Root(通过 ReactDOM.render
或 createRoot
)。如果有多个根节点,确保它们共享同一个 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/react
的 cleanup
功能:
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 树中。
排查步骤
- 定位问题来源 :在警告信息中查看完整的
Error Component Stack
,找到触发警告的具体组件。 - 检查 Context 使用 :确认是否有多个
ConfigProvider
实例或独立渲染的 Arco 组件。 - 调试渲染树:使用 React Developer Tools 检查是否有多个独立的 React 树。
- 简化代码:逐步移除部分代码,定位触发警告的最小复现案例。
示例:完整修复后的代码
以下是一个典型的列表 + 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 />);
以上全文。