ReactDOM API
ReactDOM 提供了与 DOM 交互的方法,用于在 Web 浏览器中渲染 React 组件。
概述
react-dom/client 和 react-dom 包提供了在浏览器中渲染 React 组件的方法。在 React 19+ 中,推荐使用新的 Client API。
// React 19+ (推荐)
import { createRoot } from 'react-dom/client';
// React 18
import { hydrateRoot } from 'react-dom/client';
// 旧版 API (已弃用)
import { render, hydrate } from 'react-dom';createRoot()
创建一个 React 根容器来渲染组件。这是 React 18+ 推荐的方式。
语法
const root = createRoot(container, options?)
参数
container: DOM 元素,React 将在其中渲染内容options(可选): 配置对象onRecoverableError: 错误回调函数identifierPrefix: React 生成的 id 前缀
返回值
返回一个对象,包含 render() 和 unmount() 方法。
示例
import { createRoot } from 'react-dom/client';
import App from './App';
const container = document.getElementById('root');
const root = createRoot(container);
// 渲染组件
root.render(<App />);
// 卸载组件
root.unmount();
// React 18+ 并发特性
root.render(
<StrictMode>
<App />
</StrictMode>
);✅ 优点: createRoot 支持并发特性(Concurrent Features), 如自动批处理、Suspense、Transitions 等。
hydrateRoot()
用于在服务器端渲染(SSR)的应用中"注水"(hydrate)已有的 HTML。
语法
const root = hydrateRoot(container, reactNode, options?)
示例
import { hydrateRoot } from 'react-dom/client';
import App from './App';
const container = document.getElementById('root');
hydrateRoot(
container,
<App />
);
// 带选项
hydrateRoot(
container,
<App />,
{
onRecoverableError: (error) => {
console.error('Caught error:', error);
}
}
);💡 提示: hydrateRoot 用于 SSR 应用, 它会复用服务器生成的 HTML 并添加事件监听器。
createPortal()
将子节点渲染到父组件 DOM 层次结构之外的 DOM 节点。
语法
createPortal(children, domNode, key?)
使用场景
- 模态框(Modal)
- 下拉菜单(Dropdown)
- 工具提示(Tooltip)
- 通知(Toast)
- 弹出层(Popover)
示例
import { createPortal } from 'react-dom';
function Modal({ children, isOpen }) {
if (!isOpen) return null;
return createPortal(
<div className="modal-overlay">
<div className="modal">
{children}
</div>
</div>,
document.body
);
}
// 使用
function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div>
<button onClick={() => setIsModalOpen(true)}>
打开模态框
</button>
<Modal isOpen={isModalOpen}>
<h2>模态框内容</h2>
<button onClick={() => setIsModalOpen(false)}>
关闭
</button>
</Modal>
</div>
);
}⚠️ 注意: Portal 仍然保留在 React 组件树中, 可以接收父组件的 context 和事件冒泡。
flushSync()
强制同步执行 React 更新,用于需要立即反映 DOM 更新的场景。
语法
flushSync(callback)
示例
import { flushSync } from 'react-dom';
function handleClick() {
// 立即更新 DOM
flushSync(() => {
setCount(count + 1);
});
// DOM 已更新,可以直接操作
document.getElementById('counter').scrollIntoView();
}⚠️ 警告: flushSync 会破坏并发特性, 只在绝对必要时使用。大多数情况下应该使用 ref 或 useEffect。
findDOMNode() (已弃用)
findDOMNode 已被标记为不推荐使用,应该使用 ref 替代。
// ❌ 旧方式 - 不推荐
import { findDOMNode } from 'react-dom';
const node = findDOMNode(componentRef);
// ✅ 新方式 - 推荐使用 ref
function Component() {
const ref = useRef(null);
useEffect(() => {
if (ref.current) {
// 直接访问 DOM
ref.current.scrollIntoView();
}
}, []);
return <div ref={ref}>内容</div>;
}完整示例
import { createRoot } from 'react-dom/client';
import { createPortal, useState, useRef } from 'react';
import App from './App';
// 创建根容器
const container = document.getElementById('root');
const root = createRoot(container);
// 渲染应用
root.render(<App />);
// 模态框组件
function Modal({ children }) {
const modalRoot = document.getElementById('modal-root');
return createPortal(
<div className="modal">
{children}
</div>,
modalRoot
);
}
// 使用
function App() {
const [showModal, setShowModal] = useState(false);
return (
<div>
<button onClick={() => setShowModal(true)}>
打开模态框
</button>
{showModal && (
<Modal>
<p>模态框内容</p>
<button onClick={() => setShowModal(false)}>
关闭
</button>
</Modal>
)}
</div>
);
}最佳实践
✅ 推荐做法
- 使用 createRoot 而不是 render
- 为模态框和弹窗使用 createPortal
- 使用 ref 访问 DOM 而不是 findDOMNode
- 避免使用 flushSync,使用 ref 或 useEffect
- SSR 应用使用 hydrateRoot
❌ 避免做法
- 不要使用已弃用的 render() 方法
- 不要过度使用 flushSync
- 不要在 SSR 中使用 createRoot
- 不要直接操作 DOM,优先使用 React 状态