React 18 特性

  • React 18 特性已关闭评论
  • 191 次浏览
  • A+
所属分类:Web前端
摘要

原文链接2022 年 3 月 29 日,React 18 正式版发布了。新增了以下这些新功能在 CM 模式下,React 在执行过程中,每执行一个 Fiber,都会看看有没有更高优先级的更新,如果有,则当前低优先级的的更新会被暂停,待高优先级任务执行完之后,再继续执行或重新执行。

原文链接

2022 年 3 月 29 日,React 18 正式版发布了。

新增了以下这些新功能

Concurrent Mode 并发模式

在 CM 模式下,React 在执行过程中,每执行一个 Fiber,都会看看有没有更高优先级的更新,如果有,则当前低优先级的的更新会被暂停,待高优先级任务执行完之后,再继续执行或重新执行。

startTransition

React 的状态更新可以分为两类:

  • 紧急更新(Urgent updates):比如打字、点击、拖动等,需要立即响应的行为,如果不立即响应会给人很卡,或者出问题了的感觉
  • 过渡更新(Transition updates):将 UI 从一个视图过渡到另一个视图。不需要即时响应,有些延迟是可以接受的。

默认的setState是紧急更新,startTransition包裹的setState是非紧急更新。
React 会在高优先级更新渲染完成之后,才会启动低优先级更新渲染,并且低优先级渲染随时可被其它高优先级更新中断。

// 紧急的 setInputValue(e.target.value); startTransition(() => {   setSearchQuery(input); // 非紧急的 }); 

自动批处理 Automatic Batching

批处理是指 React 将多个状态更新,聚合到一次 render 中执行,以提升性能。

在 React 18 之前,React 只会在事件回调中使用批处理,而在 Promise、setTimeout、原生事件等场景下,是不能使用批处理的。

而在 React 18 中,所有的状态更新,都会自动使用批处理,不关心场景。

function handleClick() {   setCount(c => c + 1);   setFlag(f => !f);   // React 只会 re-render 一次,这就是批处理 }  setTimeout(() => {   setCount(c => c + 1);   setFlag(f => !f);   // React 只会 re-render 一次,这就是批处理 }, 1000); 

如果你在某种场景下不想使用批处理,你可以通过 flushSync来强制同步执行

import { flushSync } from 'react-dom';  function handleClick() {   flushSync(() => {     setCounter(c => c + 1);   });   // React 更新一次 DOM   flushSync(() => {     setFlag(f => !f);   });   // React 更新一次 DOM } 

注意:自动批处理情况下在 Class 组件中,如果你在两次 setState 中间读取了 state 值,会出现不兼容的情况

// 不兼容 handleClick = () => {   setTimeout(() => {     this.setState(({ count }) => ({ count: count + 1 }));      // 在 React17 及之前,打印出来是 { count: 1, flag: false }     // 在 React18,打印出来是 { count: 0, flag: false }     console.log(this.state);      this.setState(({ flag }) => ({ flag: !flag }));   }); };  // 更改后兼容 handleClick = () => {   setTimeout(() => {     ReactDOM.flushSync(() => {       this.setState(({ count }) => ({ count: count + 1 }));     });      // 在 React18,打印出来是 { count: 1, flag: false }     console.log(this.state);      this.setState(({ flag }) => ({ flag: !flag }));   }); }; 

流式 SSR

SSR 一次页面渲染的流程大概为:

  1. 服务器 fetch 页面所需数据
  2. 数据准备好之后,将组件渲染成 string 形式作为 response 返回
  3. 客户端加载资源
  4. 客户端合成(hydrate)最终的页面内容

而在 React 18 中,基于全新的 Suspense,支持了流式 SSR,也就是允许服务端一点一点的返回页面。

在 React 18 中,我们通过 Suspense包裹,可以告诉 React,我们不需要等这个组件,可以先返回其它内容,等这个组件准备好之后,单独返回。

更多关于流式 SSR 的讲解可见:https://github.com/reactwg/react-18/discussions/37

新 Hooks

useDeferredValue

useDeferredValue 可以让一个 state 延迟生效,只有当前没有紧急更新时,该值才会变为最新值。useDeferredValue 和 startTransition 一样,都是标记了一次非紧急更新。

useId

支持同一个组件在客户端和服务端生成相同的唯一的 ID,避免 hydration 的不兼容。原理是每个 id 代表该组件在组件树中的层级结构。

useSyncExternalStore

useSyncExternalStore 能够让 React 组件在 Concurrent Mode 下安全地有效地读取外接数据源。在 Concurrent Mode 下,React 一次渲染会分片执行(以 fiber 为单位),中间可能穿插优先级更高的更新。假如在高优先级的更新中改变了公共数据(比如 redux 中的数据),那之前低优先的渲染必须要重新开始执行,否则就会出现前后状态不一致的情况。useSyncExternalStore 一般是三方状态管理库使用,一般我们不需要关注。

useInsertionEffect

这个 Hooks 只建议 css-in-js库来使用。这个 Hooks 执行时机在 DOM 生成之后,useLayoutEffect 生效之前,一般用于提前注入