【第一批吃螃蟹】试用 React 18 !

React 团队最近发布了 React 18 的 alpha 版本。这个版本主要是加强 React 应用程序的 并发渲染 能力,你能够在 React 18 中尝试体验如下几个新特性:前端

  • 新的 ReactDOM.createRoot() API(替换 ReactDOM.render()
  • 新的 startTransition API(用于非紧急状态更新)
  • 渲染的自动批处理优化(主要解决异步回调中没法批处理的问题)
  • 支持 React.lazy 的 全新 SSR 架构(支持 <Suspense> 组件)

这不,这个版本才刚刚发布社区里已经有不少小伙伴已经跃跃欲试了,我也火烧眉毛跟着社区的大佬们一块儿尝试了一下。感兴趣的小伙伴们能够一块儿跟着个人记录来试一下:react

安装 React 18 Alpha

想要在你的项目里试用 React 18 Alpha,能够尝试执行下面的命令:git

npm install react@alpha react-dom@alpha
# or
yarn add react@alpha react-dom@alpha
复制代码

若是你是使用 Create React App 初始化的项目,你可能会遇到一个因为 react-scripts 引发的 could not resolve dependency 错误:github

Could not resolve dependency:
peer react@">= 16" from react-scripts@4.0.3
复制代码

你能够在安装的时候尝试加上 --force 来解决这个问题:面试

npm install react@alpha react-dom@alpha --force
复制代码

ReactDOM.createRoot()

在 React 18 版本中,ReactDOM.createRoot() 替代了一般做为程序入口的 ReactDOM.render() 方法。npm

这个方法主要是防止 React 18 的不兼容更新致使你的应用程序崩溃。浏览器

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const container = document.getElementById('root');
// Create a root.
const root = ReactDOM.createRoot(container);
// Render the top component to the root.
root.render(<App />);
复制代码

当你更新到 React 18 时,若是你还使用 redner 函数做为程序入口,控制台会打印一个错误日志来提醒你使用 createRoot() ,只有使用了这个方法后才能使用 React 18 新功能。微信

渲染的自动批处理

React 有一道经典面试题,setState 究竟是同步的仍是异步的,我面试的时候也会常常问,具体的我在两年前的一篇文章中有介绍过:markdown

由实际问题探究setState的执行机制架构

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      val: 0
    };
  }
  
  componentDidMount() {
    this.setState({val: this.state.val + 1});
    console.log(this.state.val);   
    this.setState({val: this.state.val + 1});
    console.log(this.state.val);   

    setTimeout(() => {
      this.setState({val: this.state.val + 1});
      console.log(this.state.val); 
      this.setState({val: this.state.val + 1};
      console.log(this.state.val);  
    }, 0);
  }

  render() {
    return null;
  }
};
复制代码

好比上面的代码,咱们来考虑一下两种状况:

  • 假设 React 彻底没有批处理机制,那么执行一个 setState 就会当即触发一次页面渲染,打印顺序应该是 一、二、三、4
  • 假设 React 有一个完美的批处理机制,那么应该等整个函数执行完了以后再统一处理全部渲染,打印顺序应该是 0、0、0、0

实际上,在 React 18 版本以前,上面代码的打印顺序是 0、0、二、3

出现这个问题的主要缘由就是在 React 的事件函数和异步回调中的状态批处理机制不同。在异步回调外面,可以将全部渲染合并成一次,异步回调里面,则不会合并,会渲染屡次。

实际上,在大部分的场景下,咱们都须要在调用一个接口或者作了一些其余事情以后,再去回调函数里更新状态,上面的批处理机制就会显得很是鸡肋。

如今,React 18 版本解决了这个问题,不管你是在 Promise、setTimeout、或者其余异步回调中更新状态,都会触发批处理,上面的代码真的就会一直打印 0、0、0、0 了!

是否是很棒!React 帮咱们消灭的一道面试题 😎。

一般状况下,批处理是没什么问题的,可是有可能在某些特殊的需求(好比某个状态更改后要马上从 DOM 中获取一些内容)下不太合适,咱们可使用 ReactDOM.flushSync() 退出批处理:

import { flushSync } from 'react-dom'; // Note: react-dom, not react

function handleClick() {
  flushSync(() => {
    setCounter(c => c + 1);
  });
  // React has updated the DOM by now
  flushSync(() => {
    setFlag(f => !f);
  });
  // React has updated the DOM by now
}
复制代码

Ricky 在这篇文章(https://github.com/reactwg/react-18/discussions/21) 详细介绍了 Automatic batching ,感兴趣能够一块儿到评论区讨论。

SSR 下的懒加载支持

React.lazy 函数能让你像渲染常规组件同样处理动态引入组件。React.lazy 接受一个函数,这个函数须要动态调用 import()。它必须返回一个 Promise,该 Promise 须要 resolve 一个 default export 的 React 组件。

const MonacoEditor = React.lazy(() => import('react-monaco-editor'));
复制代码

React.lazy 必需要配合 <Suspense> 才能更好的使用,在 Suspense 组件中渲染 lazy 组件,可使用在等待加载 lazy 组件时作优雅降级(好比渲染一些 loading 效果 )。fallback 属性接受任何在组件加载过程当中你想展现的 React 元素。

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    // Displays <Spinner> until OtherComponent loads
    <React.Suspense fallback={<Spinner />}> <div> <OtherComponent /> </div> </React.Suspense>
  );
}
复制代码

React 18 之前, SSR 模式下是不支持使用 Suspense 组件的,而在 React 18 中服务端渲染的组件也支持使用 <Suspense> 了:若是你把组件包裹在了 <Suspense> 中,服务端首先会把 fallback 中的组件做为 HTML 流式传输,一旦主组件加载完成,React 会发送新的 HTML 来替换该组件。

<Layout> 
  < Article /> 
  <Suspense fallback={<Spinner />}> <Comments /> </Suspense>
 </Layout>
复制代码

好比上面的代码,<Article> 组件首先会被渲染,<Comments> 组件将被 fallback 替换为 <Spinner> 。一旦 <Comments> 组件加载完成后,React 会才将其发送到浏览器,替换 <Spinner> 组件。

Dan Abramov 在这篇文章(https://github.com/reactwg/react-18/discussions/37) 中详细介绍了这个机制,感兴趣能够到评论区一块儿讨论。

startTransition API

startTransition 是 React 18 新增长的一个 API,它可让你区分 非紧急 的状态更新。

好比如今有这样一个场景:咱们要去 Input 框输入一个值,而后下面须要同时给出经过咱们输入后的值过滤出来的一些数据。

由于你每次须要动态渲染出过滤后的值,因此你可能会将输入的值存储在一个 state 中,你的代码多是下面这样的:

setInputValue (input) ; 
setSearchQuery (input) ;
复制代码

首先用户输入上去的值确定是须要马上渲染出来的,可是过滤出来的联想数据可能不须要那么快的渲染,若是咱们不作任何额外的处理,在 React 18 以前,全部更新都会马上被渲染,若是你的原始数据很是多,那么每次输入新的值后你须要进行的计算量(根据输入的值过滤出符合条件的数据)就很是大,因此每次用户输入后可能会有卡顿现象。

因此,在之前咱们可能会本身去加一些防抖这样的操做去人为的延迟过滤数据的计算和渲染。

新的 startTransition API 可让咱们把数据标记成 transitions 状态。

import { startTransition } from 'react';


// Urgent: Show what was typed
setInputValue(input);

// Mark any state updates inside as transitions
startTransition(() => {
  // Transition: Show the results
  setSearchQuery(input);
});
复制代码

全部在 startTransition 回调中的更新都会被认为是 非紧急处理,若是出现更紧急的更新(好比用户又输入了新的值),则上面的更新都会被中断,直到没有其余紧急操做以后才会去继续执行更新。

怎么样,是否是比咱们人工实现一个防抖更优雅 😇

同时,React 还给咱们提供了一个带有 isPending 过渡标志的 Hook

import  {  useTransition  }  from  'react' ; 

const  [ isPending ,  startTransition ]  =  useTransition ( ) ;

复制代码

你可使用它和一些 loading 动画结合使用:

{ isPending  &&  < Spinner  / > }

复制代码

Ricky 在这篇文章(https://github.com/reactwg/react-18/discussions/41) 详细介绍了 startTransition ,感兴趣能够一块儿到评论区讨论。

React 18 发布计划

React 18 官方介绍(https://github.com/reactwg/react-18/discussions/4)中提到的其余两个 API useDeferredValue<SuspenseList> 还没 released ,咱们下次再用,下面是 React 18 的发布时间表:

  • React 18 Alpha 版本:如今就能用
  • 公开的 Beta 版:至少在 Alpha 版本后的几个月
  • RC 版本:至少在 Beta 版发布后的几周
  • 正式版:至少在 RC 版本发布以后的几周

参考

最后

文中若有错误,欢迎在评论区指正,若是这篇文章帮助到了你,欢迎点赞和关注。

本文首发在个人我的公众号:【code秘密花园】:试用 React 18 ! ,欢迎关注。

抖音前端正急缺人才,若是你想加入咱们,欢迎加我微信和我联系。另外若是你想加入前端、面试、理财等交流群,或者你有任何其余事情也能够添加个人我的微信 ConardLi 一块儿交流。

相关文章
相关标签/搜索