React 團隊最近發佈了 React 18 的 alpha 版本。這個版本主要是加強 React 應用程序的 併發渲染
能力,你能夠在 React 18 中嘗試體驗如下幾個新特性:前端
ReactDOM.createRoot()
API(替換 ReactDOM.render()
)startTransition
API(用於非緊急狀態更新)React.lazy
的 全新 SSR 架構(支持 <Suspense>
組件)這不,這個版本纔剛剛發佈社區裏已經有不少小夥伴已經躍躍欲試了,我也火燒眉毛跟着社區的大佬們一塊兒嘗試了一下。感興趣的小夥伴們能夠一塊兒跟着個人記錄來試一下:react
想要在你的項目裏試用 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
複製代碼
在 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
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 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
,感興趣能夠一塊兒到評論區討論。
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
是 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 官方介紹(https://github.com/reactwg/react-18/discussions/4
)中提到的其餘兩個 API useDeferredValue
、<SuspenseList>
還沒 released
,咱們下次再用,下面是 React 18 的發佈時間表:
React 18 Alpha
版本:如今就能用文中若有錯誤,歡迎在評論區指正,若是這篇文章幫助到了你,歡迎點贊和關注。
本文首發在個人我的公衆號:【code祕密花園】:試用 React 18 ! ,歡迎關注。
抖音前端正急缺人才,若是你想加入咱們,歡迎加我微信和我聯繫。另外若是你想加入前端、面試、理財等交流羣,或者你有任何其餘事情也能夠添加個人我的微信 ConardLi
一塊兒交流。