ReactDOM API

ReactDOM 提供了与 DOM 交互的方法,用于在 Web 浏览器中渲染 React 组件。

概述

react-dom/clientreact-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 状态

相关 API

这篇文章有帮助吗?