跳至主要內容

React 18Beta 版

wangdongovo...大约 8 分钟前端框架react

React 18Beta 版

简介

React 18 包括对现有功能的开箱即用的改进。它也是第一个添加对并发功能支持的 React 版本,这让您可以以 React 以前不允许的方式改善用户体验,并发功能是可选的,可以逐渐采用

安装

npm install react@beta react-dom@beta

Root API

React 17

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

React 18

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";

const container = document.getElementById("root");

const root = ReactDOM.createRoot(container);

root.render(<App />);

在 React 18 中发布遗留 API 有两个原因
希望避免用户升级到 React 18 并立即看到崩溃。相反,我们向旧的根 API 添加了一个警告,建议使用新 API。
一些应用程序可能会选择运行生产实验来比较旧根和新根,其中包括开箱即用的性能改进

开箱即用的改进

自动批处理以减少渲染

setState只在合成事件和钩子函数中是“异步”的,在原生事件和setTimeout  中都是同步的

setState  的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的“异步”,当然可以通过第二个参数  setState(partialState, callback)  中的callback拿到更新后的结果

setState  的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和 setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次setStatesetState的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时setState多个不同的值,在更新时会对其进行合并批量更新

useLayoutEffect其函数签名与  useEffect  相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect  内部的更新计划将被同步刷新。

尽可能使用标准的  useEffect  以避免阻塞视觉更新。

function fetchSome() {
  return new Promise((resolve) => setTimeout(resolve, 100));
}
const handleClick = () => {
  fetchSome().then(() => {
    setCount((c) => c + 1);
    setFlag((f) => !f);
  });
};

useLayoutEffect(() => {
  console.log("Commit");
});
console.log("Render");

如果你在同一个点击事件中有两个状态更新,React 总是将它们分批处理到一个重新渲染中。,你会看到每次点击时,React 只执行一次渲染,尽管你设置了两次状态

image.png
image.png

React 18 之前,我们只在 React 事件处理程序期间批量更新。默认情况下,React 中不会对 promise、setTimeout、本机事件处理程序或任何其他事件中的更新进行批处理。

image.png
image.png

不想自动批处理

某些代码可能依赖于在状态更改后立即从 DOM 中读取某些内容。对于这些用例,可以使用ReactDOM.flushSync()选择退出批处理

import { flushSync } from "react-dom";

const handleClick = () => {
  flushSync(() => {
    setCount((c) => c + 1);
  });

  flushSync(() => {
    setFlag((f) => !f);
  });
};
image.png
image.png

并发功能

React 18 将是第一个添加对并发功能的选择支持的 React 版本

React 18 将添加新功能,例如 startTransition、useDeferredValue、并发 Suspense 语义 SuspenseList、 等。为了支持这些功能,React 添加了协作多任务、基于优先级的渲染、调度和中断等概念。

Suspenseopen in new window

在 Legacy Suspense 和 Concurrent Suspense 中,基本的用户体验是相同的。

在 React 16.x 中添加了对 Suspense 的基本支持。但它并不完全支持 Suspense——它没有完成我们在演示中展示的所有事情,比如延迟转换(即在继续进行状态转换之前等待数据解析),或占位符限制(通过限制嵌套的、连续的占位符的出现来减少 UI 抖动)或 SuspenseList(协调组件列表或网格的外观,例如按顺序流式传输它们

用于数据的获取可以“等待”目标代码加载,并且可以直接指定一个加载页面,让它在用户等待的时候显示

<>
  <Suspense
    fallback={
      <>
        <h1>Loading 英雄联盟的英雄...</h1>
      </>
    }
  >
    <ProfileDetails resource={resource} />
    <Suspense fallback={<h1>Loading 王者荣耀的英雄...</h1>}>
      <ProfileTimeline resource={resource} />
    </Suspense>
  </Suspense>
</>
image.png
image.png
image.png
image.png

错误边界open in new window

部分 UI 的 JavaScript 错误不应该导致整个应用崩溃,为了解决这个问题,React 16 引入了一个新的概念 —— 错误边界。

错误边界是一种 React 组件,这种组件可以捕获发生在其子组件树任何位置的 JavaScript 错误,并打印这些错误,同时展示降级 UI,而并不会渲染那些发生崩溃的子组件树。错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。

注意

错误边界无法捕获以下场景中产生的错误:

  • 事件处理(了解更多open in new window
  • 异步代码(例如  setTimeout  或  requestAnimationFrame  回调函数)
  • 服务端渲染
  • 它自身抛出来的错误(并非它的子组件)

在 Suspense 中,获取数据时抛出的错误和组件渲染时的报错处理方式一样——你可以在需要的层级渲染一个错误边界组件来“捕捉”层级下面的所有的报错信息

import React, { Component } from "react";

export default class ErrorComponent extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true };
  }
  render() {
    if (this.state.hasError) {
      // 你可以自定义降级后的 UI 并渲染
      return this.props.fallback;
    }

    return this.props.children;
  }
}
<ErrorComponent fallback={<h1>子组件出错了</h1>}>
  <Suspense fallback={<h1>Loading Banner</h1>}>
    <ProfileTimeline resource={resource} />
  </Suspense>
</ErrorComponent>
image.png
image.png

SuspenseList

用于控制 Suspense 组件的现实顺序

加载顺序

together所有的 Suspense 一起展示

forwards按照顺序显示 Suspense

backwards反序展示 Suspense

hidden不显示

collapsed轮到自己在展示

<SuspenseList revealOrder="together">
  <Suspense
    fallback={
      <>
        <h1>Loading 当前时间</h1>
      </>
    }
  >
    <ProfileDetails resource={resource} />
  </Suspense>

  <Suspense fallback={<h1>Loading Banner</h1>}>
    <ProfileTimeline resource={resource} />
  </Suspense>
</SuspenseList>
image.png
image.png

踩坑

安装 rc 版本,SuspenseList 会显示找不到,从 TypeScript 编译器收到该错误,因为即使您已经安装了react@rc,您仍在使用@types/react@17SuspenseList确实存在于实现中,但编译器不知道

npm install react@rc react-dom@rc

要是报错如下呢:

image.png
image.png

解决方案如下:

npm install react@experimental react-dom@experimental

或者
如果您tsconfig.json已经"types"  在该"compilerOptions"部分中有一个数组,则添加"react/experimental"到该"types"数组中

startTransition让您在状态转换期间保持 UI 响应。

作用:标记某个更新为 transition

setTimeout不同的是,startTransition并不会延迟调度,而是会立即执行,startTransition接收的函数是同步执行的,只是这个 update 被加了一个“transitions"的标记。而这个标记,React 内部处理更新的时候是会作为参考信息的。这就意味着,相比于setTimeout, 把一个 update 交给startTransition能够更早地被处理。而在于较快的设备上,这个过度是用户感知不到的

状态更新分成两种

Urgent updates  紧急更新,指直接交互。如点击、输入、滚动、拖拽等
Transition updates  过渡更新,如 UI 从一个视图向另一个视图的更新

使用场景

startTransition可以用在任何你想更新的时候。

渲染慢:如果你有很多没那么着急的内容要渲染更新。

网络慢:如果你的更新需要花较多时间从服务端获取。

<Button type="primary" size="small" onClick={() => startTransition(() => {
    setResource(fetchProfileData())
  })}>

使用前会有 loading 展示

image.png
image.png

使用后直接过渡到数据更新后的视图,中间没有闪烁

image.png
image.png

双缓冲
image.png

useTransition

使用 startTransition 更新状态的时候,用户可能想要知道 transition 的实时情况,这个时候可以使用 React 提供的 hook api useTransition

import { useTransition } from "react";

const Button = (props) => {
  const { afresh } = props;
  const [isLoading, startTransition] = useTransition();
  return (
    <>
      <button
        disabled={isLoading}
        onClick={() => {
          startTransition(() => {
            afresh();
          });
        }}
      >
        重新获取数据
      </button>
    </>
  );
};

export default Button;

数据没有更新前,按钮为不可点击状态
image.png

useDeferredValue让您推迟更新屏幕上不太重要的部分。

相关资源链接

React 18 发布计划open in new window
React Conf open in new window
用 createRoot 替换渲染open in new window
React 18 中 Suspense 的行为变化open in new window
全新 Suspense SSR 架构open in new window

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.5