令人激动的事情出现了,近期React 官网发布了react@rc 版本,并且发布博客文章 《如何升级到 react18 RC 版本》 。文中提到了 RC 版本已经发布以及如何升级到新版本的要点. 同时鼓励大家升级尝试。
本文是一篇科普文,会介绍一些在官方文档中说明的一些要点,如果你想了解更加深入React 18 有哪些特性,可以阅读我们的往期文章。如果您想帮助我们测试 React 18,请按照本升级指南中的步骤并报告您遇到的任何问题,以便我们能够在稳定版发布之前修复这些问题。
如何安装react@rc 版本
yarn add react@rc react-dom@rc
或者
npm install react@rc react-dom@rc
重要 API变动
替换 ReactDOM.render 为 Root API
// Before
import { render } from 'react-dom';
const container = document.getElementById('app');
render(<App tab="home" />, container);
// After
import { createRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = createRoot(container);
React 18 提供了两个 root API,被称之为 Legacy Root API 和 New Root API:
Legacy Root API:是指之前版本的 root API ReactDOM.render,它将创建一个以 "legacy" 模式运行的 root,其工作方式与 React 17 完全相同。我们会给这个 API 添加一个警告,来说明它将要被弃用并建议切换到 New Root API。
New Root API:新的 root API 是 ReactDOM.createRoot。它可以在 React 18 中创建一个 root,并支持 React 18 中支持的所有新特性。
在 React 中,"root" 是一个指向顶层数据结构的指针,React 用它来跟踪要渲染的树。在 Legacy Root API 中,root 对用户来说是不透明的,因为我们将它附加到 DOM 元素上,通过 DOM 节点访问它,并没有将其暴露给用户:
我们更改这个 API 有以下几个原因。
首先,这修复了 API 在运行更新时的一些人类工程学问题。如上所示,在 Legacy API 中,你需要多次将容器元素传递给 render,即使它从未更改过。这也意味着我们不需要将根元素存储在 DOM 节点上,尽管我们今天仍然这样做。
其次,这一变化允许让我们可以移除 hydrate 方法并替换为 root 上的一个选项;删除渲染回调,这些回调在部分 hydration 中是没有意义的。
什么是 hydration ?
我们已经把 hydrate 函数移到了 hydrateRoot API 上。
before
import * as ReactDOM from 'react-dom';
import App from 'App';
const container = document.getElementById('app');
// Render with hydration.
ReactDOM.hydrate(<App tab="home" />, container);
after
import * as ReactDOM from 'react-dom';
import App from 'App';
const container = document.getElementById('app');
// Create *and* render a root with hydration.
const root = ReactDOM.hydrateRoot(container, <App tab="home" />);
// Unlike with createRoot, you don't need a separate root.render() call here
注意,与 createRoot 不同,hydrateRoot 接受原生 JSX 作为第二个参数。这是因为初始客户端渲染是特殊的,需要与服务器树匹配。
如果你想在 hydration 后再次更新 root,你可以将它保存到一个变量中,就像使用 createRoot 一样,然后调用 root.render():
import * as ReactDOM from 'react-dom';
import App from 'App';
const container = document.getElementById('app');
// Create *and* render a root with hydration.
const root = ReactDOM.hydrateRoot(container, <App tab="home" />);
// You can later update it.
root.render(<App tab="profile" />);
服务端渲染 API 的更新
在React@rc版本中,React 为了支持服务端的 Suspense 和SSR,改进了 react-dom/server,不支持 Suspense 的 Node.js 流式 API 将会被完全弃用:
renderToNodeStream 被弃用
针对 Node 环境中的流媒体,使用:renderToPipeableStream。同时引入了一个新的 API renderToReadableStream,用于流式 SSR 和 Suspense,并为现代边缘运行环境提供支持,比如 Deno 和 Cloudflare workers:renderToString、renderToStaticMarkup 这两个 API 还可以继续用,但是对 Suspense 支持就不那么友好了。
最后,renderToStaticNodeStream这个 API 将继续使用,用于渲染电子邮件
Automatic Batching
由于架构设计问题,在 React 18 之前,React 只能在组件生命周期函数或合成事件函数中进行批处理。在默认情况下,Promise、setTimeout 以及异步回调是无法获得批处理的优化。
批处理是说 React 将多个状态更新合并到一个重新渲染中,以此来获得更好的性能。在 React 18 before,react 将一个事件中的多个 setState 合并为一个进行处理,在 promises、 setTimeout、和其他异步事件的更新没有合并。
// React 18 before
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// 在合成事件中,享受批处理优化,只会重新渲染一次
}
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// 不会进行批处理,会触发两次重新渲染
}, 1000);
// React 18
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// 只会重新渲染一次
}
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// 只会重新渲染一次
}, 1000);
新的API的变化
useSyncExternalStore将替代useMutableSource 用于订阅外部源,允许外部存储通过强制同步更新来支持并发读取。这个新的 API 推荐用于任何与 React 外部状态集成的库
import {useSyncExternalStore} from 'react';
// 基础用法,getSnapshot返回一个缓存的值
const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
// 根据数据字段,使用内联的getSnapshot返回缓存的数据
const selectedField = useSyncExternalStore(store.subscribe, () => store.getSnapshot().selectedField);
useId用于在客户端与服务端之间产生唯一ID,避免SSR hydrate时元素不匹配,这可以解决 React 17 以及更低版本的问题。
function Checkbox() {
const id = useId();
return (
<>
<label htmlFor={id}>Do you like React?</label>
<input type="checkbox" name="react" id={id} />
</>
);
);
useInsertionEffect工作原理类似useLayoutEffect,区别在于回调执行时还不能访问ref中的DOM节点。它可以解决 CSS-in-JS 库在渲染中动态注入样式的性能问题。
function useCSS(rule) {
useInsertionEffect(() => {
if (!isInserted.has(rule)) {
isInserted.add(rule);
document.head.appendChild(getStyleForRule(rule));
}
});
return rule;
}
function Component() {
let className = useCSS(rule);
return <div className={className} />;
}
更新严格模式 (Strict Mode)
未来,React期望添加一个这样的特性,允许 React 添加和删除 UI 部分,同时保留所有的状态。例如,当用户选项卡远离屏幕并返回时候,React 应该能够立即显示前一个屏幕。为此,React 将使用与前面相同的组件状态卸载和重新挂载树。
此特性将使 React 具有更好的开箱即用的性能,但是需要组件对多次挂载和销毁的效果具有弹性。多数效果不需要任何改变就可以工作,但有些效果假设它们只被安装或者销毁一次。
为了帮助表面这些问题,react 18 引入了一个新的开发-只检查严格模式。每当一个组件第一次挂载时,这个新的检查将自动卸载和重新挂载每个组件,恢复第二次挂载时以前的状态。
配置你的测试环境
为了解决 The current testing environment is not configured to support act(…)的警告,你可以设置
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
设置完后,React将在类似单元测试的环境中运行,当然,如果测试库可以自动为你配置这个功能就太棒了。例如:下一个版本的 React Testing Library 内置了对 React 18的支持
不再支持 IE 浏览器
在本次版本中,React 不再对 Internet Explorer 进行支持,它会在2022年6月15日失去支持。React 之所以做出这个改变,是因为在 React 18 引入的新特性是使用现代浏览器的特性构建的,比如微任务等,这些特性在 IE 中无法充分填充与支持。
其他变化
更新以删除“setState on unmounted component” 警告
Suspense不再需要fallbackprop 来捕捉
组件现在可以渲染 undefined
弃用 renderSubtreeIntoContainer
StrictMode 更新为默认情况下不会静默双重日志记录
如果大家想了解更多内容,欢迎查看 React 官方博客:https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html
源码
最后, 送人玫瑰,手留余香,觉得有收获的朋友可以点赞,关注一波 ,相关 文档资料 已上传至 React 中文社区交流群, 请关注公众号,点击菜单栏 入群学习,入群就送前端精选100本电子书 添加 入群小助手 后入群领取。陆续更新前端超硬核文章。
推荐阅读
(点击标题可跳转阅读)
[面试必问]-你不知道的 React Hooks 那些糟心事
[面试必问]-全网最全 React16.0-16.8 特性总结
[架构分享]- 微前端qiankun+docker+nginx自动化部署
[架构分享]-石墨文档 Websocket 百万长连接技术实践
觉得本文对你有帮助?请分享给更多人
关注「React中文社区」加星标,每天进步