hydrateRoot
函数允许你在先前由 react-dom/server
生成的浏览器 HTML DOM 节点中展示 React 组件。
const root = hydrateRoot(domNode, reactNode, options?)
参考
hydrateRoot(domNode, reactNode, options?)
用 hydrateRoot
函数将 React 连接到由 React 在服务端环境中渲染的现有 HTML 中。
import { hydrateRoot } from 'react-dom/client';
const domNode = document.getElementById('root');
const root = hydrateRoot(domNode, reactNode);
React 将会连接到内部有 domNode
的 HTML 上,然后接管其中的 domNode
。一个完全由 React 构建的应用只会在其根组件中调用一次 hydrateRoot
方法。
参数
-
domNode
:一个在服务器端渲染时呈现为根元素的 DOM 元素。 -
reactNode
:用于渲染已存在 HTML 的“React 节点”。这个节点通常是一些类似于<App />
的 JSX,它会在ReactDOM Server
端使用类似于renderToPipeableStream(<App />)
的方法进行渲染。 -
可选
options
:一个包含此 React 根元素选项的对象。- optional
onCaughtError
: Callback called when React catches an error in an Error Boundary. Called with theerror
caught by the Error Boundary, and anerrorInfo
object containing thecomponentStack
. - optional
onUncaughtError
: Callback called when an error is thrown and not caught by an Error Boundary. Called with theerror
that was thrown and anerrorInfo
object containing thecomponentStack
. - optional
onRecoverableError
: Callback called when React automatically recovers from errors. Called with theerror
React throws, and anerrorInfo
object containing thecomponentStack
. Some recoverable errors may include the original error cause aserror.cause
. - optional
identifierPrefix
: A string prefix React uses for IDs generated byuseId
. Useful to avoid conflicts when using multiple roots on the same page. Must be the same prefix as used on the server.
- optional
返回值
hydrateRoot
返回一个包含两个方法的对象 render
和 unmount
。
警告
hydrateRoot()
期望渲染内容与服务端渲染的内容完全相同。你应该将不匹配视为错误并进行修复。- 在开发模式下,React 会在激活期间发出不匹配警告。在不匹配的情况下,不能保证内容差异会被修补。出于性能原因,这很重要,因为在大多数应用程序中,不匹配很少见,因此验证所有标记将是昂贵而不可行的。
- 你的应用程序可能只有一个
hydrateRoot()
函数调用。如果你使用框架,则可能会为你完成此调用。 - 如果你的应用程序是客户端渲染,并且没有已渲染好的 HTML,则不支持使用
hydrateRoot()
。请改用createRoot()
。
root.render(reactNode)
使用 root.render
更新一个激活根组件中的 React 组件来渲染浏览器端 DOM 元素。
root.render(<App />);
React 将会在激活 root
中更新 <App />
。
参数
reactNode
:你想要更新的 “React 节点”。通常这会是一段JSX代码,例如<App />
,但你也可以传递一个通过createElement()
创建的 React 元素,一个字符串,一个数字,null
值 或者undefined
值。
返回值
root.render
返回 undefined
值。
警告
- 如果你在根节点还没有完成激活的情况下调用了
root.render
,React 将清除现有的服务端渲染 HTML 内容,并将整个根节点切换到客户端渲染。
root.unmount()
调用 root.unmount
来销毁 React 根节点内的渲染树。
root.unmount();
完全使用 React 构建的应用通常不会有任何调用 root.unmount
的情况。
这主要适用于 React 根节点的 DOM 节点(或其任何祖先节点)可能会被其他代码从 DOM 中移除的情况。例如,想象一下一个 jQuery 标签面板,它会将非活动标签从 DOM 中移除。如果一个标签被移除,其内部的所有内容(包括其中的 React 根节点)也将从 DOM 中移除。你需要调用 root.unmount
来告诉 React “停止”管理已移除根节点的内容。否则,已移除根节点内的组件将无法清理和释放已使用的资源,例如订阅。
调用 root.unmount
将卸载根节点中的所有组件,并“分离” React 与根 DOM 节点之间的连接,包括删除树中的任何事件处理程序或状态。
参数
root.unmount
不接受任何参数。
返回值
render
返回 undefined
值。
警告
-
调用
root.unmount
将卸载树中的所有组件,并“分离” React 与根 DOM 节点之间的连接。 -
一旦你调用
root.unmount
,就不能再在根节点上调用root.render
。在未挂载的根节点上尝试调用root.render
将抛出“不能更新未挂载的根节点”的错误。
用法
激活服务端渲染的 HTML
如果你的应用程序的 HTML 是由 react-dom/server
生成的,你需要在客户端上进行 hydrate。
import { hydrateRoot } from 'react-dom/client';
hydrateRoot(document.getElementById('root'), <App />);
对于你的应用程序来说,这将激活你的服务端 HTML 来复苏里面的 浏览器 DOM 节点和 React 组件。通常,你只需要在启动时执行一次。如果你使用框架,则可能会自动在幕后执行此操作。
为了进行激活,React 将把你的组件逻辑连接到服务器上生成的初始 HTML 中。激活可以将来自服务器的初始 HTML 快照转换为在浏览器中运行的完全可交互应用。
import './styles.css'; import { hydrateRoot } from 'react-dom/client'; import App from './App.js'; hydrateRoot( document.getElementById('root'), <App /> );
你不需要再次调用 hydrateRoot
或者在其他地方调用它。从现在开始,React 将管理你的应用程序的 DOM。想要更新 UI 请使用 useState 替代。
激活整个文档
完全使用 React 构建的应用程序可以将整个文档作为 JSX 渲染,包括 <html>
标签:
function App() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/styles.css"></link>
<title>My app</title>
</head>
<body>
<Router />
</body>
</html>
);
}
要对整个文档进行激活处理,将全局的 document
作为 hydrateRoot
的第一个参数传递:
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(document, <App />);
抑制不可避免的激活处理不匹配错误
如果一个单独元素属性或文本内容在服务器和客户端之间是不可避免地不同的(例如,时间戳),则可以抑制激活处理不匹配警告。
要消除对元素的激活处理警告,请添加 suppressHydrationWarning={true}
:
export default function App() { return ( <h1 suppressHydrationWarning={true}> Current Date: {new Date().toLocaleDateString()} </h1> ); }
此方法仅适用于当前层级,并且旨在作为一种脱围机制。不要滥用它。除非是文本内容,否则 React 不会尝试修补它,因此可能会保持不一致,直到未来的更新来到。
处理不同的客户端和服务端内容
如果你有意在服务器和客户端上呈现不同的内容,则可以进行两次渲染。在客户端上呈现不同内容的组件可以读取类似于 isClient
的 状态变量,你可以在 Effect 中将其设置为 true
:
import { useState, useEffect } from "react"; export default function App() { const [isClient, setIsClient] = useState(false); useEffect(() => { setIsClient(true); }, []); return ( <h1> {isClient ? 'Is Client' : 'Is Server'} </h1> ); }
这样,初始渲染将呈现与服务器相同的内容,避免不匹配,但是在激活之后会同步进行额外的渲染。
更新激活根组件
在根组件激活完成之后,你可以调用 root.render
来更新根 React 组件。与 createRoot
不同的是,通常你不需要这样做,因为初始内容已经渲染为 HTML。
如果在激活之后某个时刻调用了 root.render
,并且组件树结构与之前渲染的相匹配,那么 React 将 保留重置 state。请注意,你可以在输入框中输入文字,这意味着在此示例中每秒钟重复调用的 render
不会破坏已有的组件状态:
import { hydrateRoot } from 'react-dom/client'; import './styles.css'; import App from './App.js'; const root = hydrateRoot( document.getElementById('root'), <App counter={0} /> ); let i = 0; setInterval(() => { root.render(<App counter={i} />); i++; }, 1000);
It is uncommon to call root.render
on a hydrated root. Usually, you’ll update state inside one of the components instead.
Show a dialog for uncaught errors
By default, React will log all uncaught errors to the console. To implement your own error reporting, you can provide the optional onUncaughtError
root option:
import { hydrateRoot } from 'react-dom/client';
const root = hydrateRoot(
document.getElementById('root'),
<App />,
{
onUncaughtError: (error, errorInfo) => {
console.error(
'Uncaught error',
error,
errorInfo.componentStack
);
}
}
);
root.render(<App />);
The onUncaughtError option is a function called with two arguments:
- The error that was thrown.
- An errorInfo object that contains the componentStack of the error.
You can use the onUncaughtError
root option to display error dialogs:
import { hydrateRoot } from "react-dom/client"; import App from "./App.js"; import {reportUncaughtError} from "./reportError"; import "./styles.css"; import {renderToString} from 'react-dom/server'; const container = document.getElementById("root"); const root = hydrateRoot(container, <App />, { onUncaughtError: (error, errorInfo) => { if (error.message !== 'Known error') { reportUncaughtError({ error, componentStack: errorInfo.componentStack }); } } });
Displaying Error Boundary errors
By default, React will log all errors caught by an Error Boundary to console.error
. To override this behavior, you can provide the optional onCaughtError
root option for errors caught by an Error Boundary:
import { hydrateRoot } from 'react-dom/client';
const root = hydrateRoot(
document.getElementById('root'),
<App />,
{
onCaughtError: (error, errorInfo) => {
console.error(
'Caught error',
error,
errorInfo.componentStack
);
}
}
);
root.render(<App />);
The onCaughtError option is a function called with two arguments:
- The error that was caught by the boundary.
- An errorInfo object that contains the componentStack of the error.
You can use the onCaughtError
root option to display error dialogs or filter known errors from logging:
import { hydrateRoot } from "react-dom/client"; import App from "./App.js"; import {reportCaughtError} from "./reportError"; import "./styles.css"; const container = document.getElementById("root"); const root = hydrateRoot(container, <App />, { onCaughtError: (error, errorInfo) => { if (error.message !== 'Known error') { reportCaughtError({ error, componentStack: errorInfo.componentStack }); } } });
Show a dialog for recoverable hydration mismatch errors
When React encounters a hydration mismatch, it will automatically attempt to recover by rendering on the client. By default, React will log hydration mismatch errors to console.error
. To override this behavior, you can provide the optional onRecoverableError
root option:
import { hydrateRoot } from 'react-dom/client';
const root = hydrateRoot(
document.getElementById('root'),
<App />,
{
onRecoverableError: (error, errorInfo) => {
console.error(
'Caught error',
error,
error.cause,
errorInfo.componentStack
);
}
}
);
The onRecoverableError option is a function called with two arguments:
- The error React throws. Some errors may include the original cause as error.cause.
- An errorInfo object that contains the componentStack of the error.
You can use the onRecoverableError
root option to display error dialogs for hydration mismatches:
import { hydrateRoot } from "react-dom/client"; import App from "./App.js"; import {reportRecoverableError} from "./reportError"; import "./styles.css"; const container = document.getElementById("root"); const root = hydrateRoot(container, <App />, { onRecoverableError: (error, errorInfo) => { reportRecoverableError({ error, cause: error.cause, componentStack: errorInfo.componentStack }); } });
Troubleshooting
I’m getting an error: “You passed a second argument to root.render”
A common mistake is to pass the options for hydrateRoot
to root.render(...)
:
To fix, pass the root options to hydrateRoot(...)
, not root.render(...)
:
// 🚩 Wrong: root.render only takes one argument.
root.render(App, {onUncaughtError});
// ✅ Correct: pass options to createRoot.
const root = hydrateRoot(container, <App />, {onUncaughtError});