概览
在应用顶层使用的 DOM(DOM-specific)方法
用于 React 模型以外的地方
浏览器支持
React 支持所有的现代浏览器,包括 IE9 及以上版本,但是需要为旧版浏览器比如 IE9 和 IE10 引入相关的 polyfills 依赖
API
-
render()(已废弃)ReactDOM.render(element, container[, callback])-
container 里渲染 element 元素或者组件,并返回对该组件的引用(函数组件和React 元素返回 null)
推荐为根元素添加 callback ref
-
回调函数在组件被渲染或更新之后被执行
-
ReactDOM.render() 首次调用时,容器节点里的所有 DOM 元素都会被替换
后续的调用则会使用diff进行高效的更新
-
ReactDOM.render() 不会修改容器节点
- 只会修改容器的子节点
- 可以在不覆盖现有子节点的情况下,将组件插入已有的 DOM 节点中
-
-
hydrate()(已废弃)ReactDOM.hydrate(element, container[, callback]) -
unmountComponentAtNode()(已废弃)ReactDOM.unmountComponentAtNode(container)-
从 DOM 中卸载组件,会将其事件处理器(event handlers)和 state 一并清除
-
组件被移除将会返回
true,如果没有组件可被移除将会返回false
-
-
findDOMNode()(已废弃)-
返回浏览器中相应的原生 DOM 元素
-
当组件渲染的内容为
null或false时,findDOMNode也会返回null -
当组件渲染的是字符串时,
findDOMNode返回的是字符串对应的 DOM 节点 -
返回有多个子节点的 fragment,
findDOMNode会返回第一个非空子节点对应的 DOM 节点。 -
findDOMNode不能用于函数组件
-
flushSyncflushSync(callback)强制同步更新回调函数内容
-
影响性能尽量不用
-
Suspense 可能展示 fallback
-
可以使用副作用返回之前同步应用它们所包含的任何更新
-
当需要刷新 callback 内部的更新时,
flushSync可能更新 callback 外部更新例如,按钮点击以后需要立刻更新 DOM
onbeforeprintimport { useState, useEffect } from 'react'; import { flushSync } from 'react-dom'; export default function PrintApp() { const [isPrinting, setIsPrinting] = useState(false); useEffect(() => { function handleBeforePrint() { flushSync(() => { setIsPrinting(true); }) } function handleAfterPrint() { setIsPrinting(false); } window.addEventListener('beforeprint', handleBeforePrint); window.addEventListener('afterprint', handleAfterPrint); return () => { window.removeEventListener('beforeprint', handleBeforePrint); window.removeEventListener('afterprint', handleAfterPrint); } }, []); return ( <> <h1>isPrinting: {isPrinting ? 'yes' : 'no'}</h1> <button onClick={() => window.print()}> Print </button> </> ); }
-
-
createElement()(已过时)创建 React 元素
React.createElement( type, [props], [...children] )type:标签名、组件名、React fragment
props:props对象
children:DOM结构、组件名、数组
-
cloneElement()(已过时)克隆element元素
config定义新props,key,ref,如果没有定义使用原始元素props,key,ref
React.cloneElement( element, [config], [...children] ) <element.type {...element.props} {...props}>{children}</element.type> -
isValidElement()(已过时)验证对象是否为 React 元素
React.isValidElement(object) -
React.Children相关函数(已过时)处理
this.props.childrenReact.Children.map(children, function[(thisArg)])- 如果
children是一个数组,它将被遍历并为数组中的每个子节点调用该函数。 - 如果子节点为
null或是undefined,则此方法将返回null或是undefined,而不会返回数组 - 如果
children是一个Fragment对象,它将被视为单一子节点的情况处理,而不会被遍历 React.Children.forEach(children, function[(thisArg)])React.Children.count(children)React.Children.only(children)- 验证
children是否只有一个子节点(一个 React 元素),如果有则返回它,否则此方法会抛出错误 React.Children.only()不接受[React.Children.map()](https://zh-hans.reactjs.org/docs/react-api.html#reactchildrenmap)的返回值,因为它是一个数组而并不是 React 元素React.Children.toArray(children)- 将
children这个复杂的数据结构以数组的方式扁平展开并返回,并为每个子节点分配一个 key - 向下传递
this.props.children之前对内容重新排序或获取子集
-
ReactDOMClientimport * as ReactDOM from 'react-dom/client';-
createRoot()const root = createRoot(domNode, options?)-
创建root节点,管理 DOM
-
不会修改 domNode 节点
-
客户端
-
想要渲染一个不是子组件时使用 createPortal
-
options
-
onRecoverableErrorReact从错误恢复时执行
-
identifierPrefix-
id前缀,React.useId,同一页面使用多个根节点避免冲突
-
服务端必须是相同前缀
-
-
-
root.render(reactNode)
reactNode 通常是一段 JSX 也可以是使用
createElement()创建的 -
第一次运行会清除已有的 HTML
-
如果是服务端渲染或者构建时 HTML,使用 hydrateRoot
-
同一个根节点多次运行 render 时会按照 React 相关更新规则
-
root.unmount();
-
销毁已经渲染的内容
-
第三方删除节点的时候使用
-
-
只会执行一次
-
全部使用 React 渲染
-
部分使用 React 渲染
-
更新根组件
多次调用 Render 函数,组件结构没有变化 state 会保留
问答
-
创建了根组件,但是没有内容显示
忘记了 root.render()
-
Target container is not a DOM element
- domNode 不是 DOM 节点
- ID 不匹配
- script 标签没有渲染任何节点
- createRoot(domNode) 写成了 createRoot()
-
Functions are not valid as a React child
// 🚩 Wrong: App is a function, not a Component. root.render(App); // ✅ Correct: <App /> is a component. root.render(<App />);-
服务端渲染的 HTML 重新创建
使用 hydrateRoot
-
-
hydrateRoot()const root = hydrateRoot(domNod, reactNode, options?)react-dom/server生成 HTML 节点,使用 hydrateRoot 进行展示domNode 服务器上作为根节点渲染
renderToPipeableStream(<App />)reactNode 一段 JSX 使用服务器渲染
-
options
-
onRecoverableErrorReact从错误恢复时执行
-
identifierPrefix-
id前缀,React.useId
-
服务端必须是相同前缀
-
-
一定解决不匹配的错误提示
-
-
注意事项
hydrateRoot() 期望渲染内容和服务端渲染的内容一样
开发模式如果客户端和服务端代码不一致会提醒
只有一个 hydrateRoot
如果是客户端渲染使用 createRoot()
root.render()
root.unmount()
HTML 是由 react-dom/server 生成的需要在客户端使用 hydrate
Hydrating 整个文档
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>
);
}
import { hydrateRoot } from 'react-dom/client';
import App from './App.js';
hydrateRoot(document, <App />);
减少 hydration 出现不可避免的错误
timestamp 时间戳,忽略提示
export default function App() {
return (
<h1 suppressHydrationWarning={true}>
Current Date: {new Date().toLocaleDateString()}
</h1>
);
}
隐藏客户端和服务端不同的内容
客户端渲染和服务端不同的内容,使用都可以通过的 render
速度慢需要渲染两次
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>
);
}
更新已经 hydrated 组件
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);