日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
一篇帶給你React18升級(jí)指南

React 18 RC.3 版已經(jīng)發(fā)布,并且 API 已經(jīng)穩(wěn)定下來(lái),現(xiàn)在主要是一些 BUG 修復(fù),相信不久后便會(huì)發(fā)布正式版。React 團(tuán)隊(duì)對(duì)新特性的探索相當(dāng)謹(jǐn)慎,距離 16.8 版本已經(jīng)有 3 年時(shí)間了,完全版的并發(fā)模式終于到來(lái)。今天我們從使用者的角度來(lái)探索下 React 17 升級(jí)到 18 會(huì)遇到的問(wèn)題和一些新增的功能。

升級(jí)

使用 yarn 要安裝最新的 React 18 RC

yarn add react@rc react-dom@rc

變更

React 18 已經(jīng)放棄對(duì) IE 11 的支持,有兼容 IE 的需求則使用 React 17。

createRoot

React 18 提供了兩個(gè)根 API,我們稱之為 Legacy Root API 和 New Root API。

  • Legacy root API:即 ReactDOM.render。這將創(chuàng)建一個(gè)以“遺留”模式運(yùn)行的 root,其工作方式與 React 17 完全相同。使用此 API 會(huì)有一個(gè)警告,表明它已被棄用并切換到 New Root API。
  • New Root API:即 createRoot。這將創(chuàng)建一個(gè)在 React 18 中運(yùn)行的 root,它添加了 React 18 的所有改進(jìn)并允許使用并發(fā)功能。

我們以 Vite + TS 作為腳手架啟動(dòng)項(xiàng)目。項(xiàng)目啟動(dòng)后你會(huì)在控制臺(tái)中看到一個(gè)警告:

也就意味著你可以直接將項(xiàng)目升級(jí)到 React 18 版本而不會(huì)直接造成 break change。因?yàn)樗鼉H僅給予了一個(gè)警告,并且在整個(gè) 18 版本中都為可用兼容狀態(tài),并保持著 React 17 版本的特性。

為什么要這樣做呢?因?yàn)閮H僅為項(xiàng)目升級(jí)的話比較干脆利落,遇見(jiàn)一個(gè)地方改一個(gè)地方,無(wú)歷史包袱。但是 React 組件生態(tài)非常龐大,很多組件會(huì)用到 ReactDOM.render 直接渲染,比如常見(jiàn) UI 庫(kù)中的 Modal.confirm 類似的 API,這時(shí)就需要一個(gè)版本的周期讓這些生態(tài)組件升級(jí)上來(lái)。

// React 17
import ReactDOM from'react-dom';
const container = document.getElementById('app');
// 裝載
ReactDOM.render(, container);
// 卸載
ReactDOM.unmountComponentAtNode(container);

// React 18
import { createRoot } from'react-dom/client';
const container = document.getElementById('app');
const root = createRoot(container);
// 裝載
root.render();
// 卸載
root.unmount();

還不得不說(shuō) createRoot API 和 Vue3 的 createApp 形式一模一樣。

FAQ: 在 TypeScript, createRoot 中參數(shù) container 可接收 HTMLElement ,但不能為空。使用要么斷言,要么加判斷吧~

服務(wù)端渲染

hydrateRoot

如果的應(yīng)用使用帶注水的服務(wù)端渲染,請(qǐng)升級(jí) hydrate 到 hydrateRoot。

const root = hydrateRoot(container, );
// 這里無(wú)需執(zhí)行 root.render

在此版本中,也改進(jìn)了 react-dom/serverAPI 以完全支持服務(wù)器上的 Suspense 和流式 SSR。作為這些更改的一部分,將棄用舊的 Node 流式 API,它不支持服務(wù)器上的增量 Suspense 流式傳輸。

  • renderToNodeStream => renderToPipeableStream。
  • 新增 renderToReadableStream 以支持 Deno。
  • 繼續(xù)使用 renderToString (對(duì) Suspense 支持有限)。
  • 繼續(xù)使用 renderToStaticMarkup (對(duì) Suspense 支持有限)。

setState 同步/異步

這是 React 此次版本中最大的破壞性更新,并且無(wú)法向下兼容。

React 中的批處理簡(jiǎn)單來(lái)說(shuō)就是將多個(gè)狀態(tài)更新合并為一次重新渲染,以獲得更好的性能,在 React 18 之前,React 只能在組件的生命周期函數(shù)或者合成事件函數(shù)中進(jìn)行批處理。默認(rèn)情況下,Promise、setTimeout 以及原生事件中是不會(huì)對(duì)其進(jìn)行批處理的。如果需要保持批處理,則可以用 unstable_batchedUpdates 來(lái)實(shí)現(xiàn),但它不是一個(gè)正式的 API。

React 18 之前:

function handleClick() {
setCount(1);
setFlag(true);
// 批處理:會(huì)合并為一次 render
}
asyncfunction handleClick() {
await setCount(2);
setFlag(false);
// 同步模式:會(huì)執(zhí)行兩次 render
// 并且在 setCount 后,在 setFlag 之前能通過(guò) Ref 獲取到最新的 count 值
}

在 React 18 上面的第二個(gè)例子只會(huì)有一次 render,因?yàn)樗械母露紝⒆詣?dòng)批處理。這樣無(wú)疑是很好的提高了應(yīng)用的整體性能。

flushSync

如果我想在 React 18 退出批處理該怎么做呢?官方提供了一個(gè) API flushSync。

flushSync(fn: () => R): R 它接收一個(gè)函數(shù)作為參數(shù),并且允許有返回值。

function handleClick() {
flushSync(() => {
setCount(3);
});
// 會(huì)在 setCount 并 render 之后再執(zhí)行 setFlag
setFlag(true);
}

注意:flushSync 會(huì)以函數(shù)為作用域,函數(shù)內(nèi)部的多個(gè) setState 仍然為批量更新,這樣可以精準(zhǔn)控制哪些不需要的批量更新:

function handleClick() {
flushSync(() => {
setCount(3);
setFlag(true);
});
// setCount 和 setFlag 為批量更新,結(jié)束后
setLoading(false);
// 此方法會(huì)觸發(fā)兩次 render
}

這種方式會(huì)比 React 17 及以前的方式更優(yōu)雅的顆粒度控制 rerender。

flushSync 再某些場(chǎng)景中非常有用,比如在點(diǎn)擊一個(gè)表單中點(diǎn)擊保存按鈕,并觸發(fā)子表單關(guān)閉,并同步到全局 state,狀態(tài)更新后再調(diào)用保存方法:

子表單:

exportdefaultfunction ChildForm({ storeTo }) {
const [form] = Form.useForm();

// 當(dāng)前組件卸載時(shí)將子表單的值同步到全局
// 若要觸發(fā)父組件同步 setState,必須使用 useLayoutEffect
useLayoutEffect(() => {
return() => {
storeTo(form.getFieldsValue());
};
}, []);

return (





);
}

外部容器:

  onClick={() => {
// 觸發(fā)子表單卸載關(guān)閉
flushSync(() => setVisible(false));
// 子表單值更新到全局后,觸發(fā)保存方法,可以保證 onSave 獲取到最新填寫(xiě)的表單值
onSave();
}}
>
保存

{visible && }


不過(guò) unstable_batchedUpdates 在 React 18 中將繼續(xù)保留整個(gè)版本,因?yàn)樵S多開(kāi)源庫(kù)用了它。

已卸載組件更新?tīng)顟B(tài)警告

我們?cè)谡i_(kāi)發(fā)時(shí)難免會(huì)出現(xiàn)以下錯(cuò)誤:

這個(gè)警告被廣泛誤解并且有些誤導(dǎo)。原本旨在針對(duì)如下場(chǎng)景:

useEffect(() => {
function handleChange() {
setState(store.getState());
}
store.subscribe(handleChange);
return() => store.unsubscribe(handleChange);
}, []);

如果您忘記了 unsubscribe 效果清理中的調(diào)用,則會(huì)發(fā)生內(nèi)存泄漏。在實(shí)踐中,上述情況并不常見(jiàn)。這在我們的代碼中更為常見(jiàn):

asyncfunction handleSubmit() {
setLoading(true);
// 在我們等待時(shí)組件可能會(huì)卸載
await post('/some-api');
setLoading(false);
}

在這里,警告也會(huì)觸發(fā)。但是,在這種情況下,警告具有誤導(dǎo)性。

這里沒(méi)有實(shí)際的內(nèi)存泄漏,Promise 會(huì)很快 resolve,之后它可以被垃圾回收。為了抑制這個(gè)警告,我們可能會(huì)寫(xiě)很多 isMounted 無(wú)用的判斷,會(huì)使代碼變得更加復(fù)雜。

在 React 18 中這個(gè)警告已經(jīng)被移除掉了。

組件返回 null

在 React 17 中,如果組件在 render 中返回了 undefined,React 會(huì)在運(yùn)行時(shí)拋出一個(gè)錯(cuò)誤:

function Demo() {
return undefined;
}

這里我們可以把 undefined 換成 null,程序?qū)⒗^續(xù)運(yùn)行。此行為的目的是幫助用戶發(fā)現(xiàn)意外忘記 return 語(yǔ)句的常見(jiàn)問(wèn)題。對(duì)于 React 18 的 Suspense fallback 會(huì)出現(xiàn) undefined 而不報(bào)錯(cuò)從而導(dǎo)致出現(xiàn)不一致。

現(xiàn)在類型系統(tǒng)和 Eslint 都非常健壯可以很好避免這類低級(jí)錯(cuò)誤,因此 React 18 不再檢查因返回 undefined 而導(dǎo)致崩潰。

StrictMode

從 React 17 開(kāi)始,React 會(huì)自動(dòng)修改控制臺(tái)方法,例如 console.log() 在第二次調(diào)用生命周期函數(shù)時(shí)使日志靜音。但是,在某些可以使用變通方法的情況下,它可能會(huì)導(dǎo)致不良行為。

這這種行為在 React 18 中已經(jīng)移除,如果安裝了 React DevTools > 4.18.0,那么第二次渲染期間的日志現(xiàn)在將以柔和的顏色顯示在控制臺(tái)中。

新 API

useSyncExternalStore

useSyncExternalStore 經(jīng)歷了一次修改,由 unstable_useMutableSource 改變而來(lái),用于訂閱外部數(shù)據(jù)源。主要幫助有外部 store 需求的開(kāi)發(fā)者解決撕裂問(wèn)題。

一個(gè)監(jiān)聽(tīng) innerWidth 變化的 hook 最簡(jiǎn)單例子:

import { useMemo, useSyncExternalStore } from'react';

function useInnerWidth(): number {
// 保持 subscribe 固定引用,避免 resize 監(jiān)聽(tīng)器重復(fù)執(zhí)行
const [subscribe, getSnapshot] = useMemo(() => {
return [
(notify: () =>void) => {
// 真實(shí)情況這里會(huì)用到節(jié)流
window.addEventListener('resize', notify);
return() => {
window.removeEventListener('resize', notify);
};
},
// 返回 resize 后需要的快照
() => window.innerWidth,
];
}, []);
return useSyncExternalStore(subscribe, getSnapshot);
}
function WindowInnerWidthExample() {
const width = useInnerWidth();

return

寬度: {width}

;
}

Demo 地址:??https://codesandbox.io/s/usesyncexternalstore-demo-q47kyn。??

React 自身 state 已經(jīng)原生的解決的并發(fā)特性下的撕裂(tear) 問(wèn)題。useSyncExternalStore 主要對(duì)于框架開(kāi)發(fā)者,比如 redux,它在控制狀態(tài)時(shí)可能并非直接使用的 React 的 state,而是自己在外部維護(hù)了一個(gè) store 對(duì)象,脫離了 React 的管理,也就無(wú)法依靠 React 自動(dòng)解決撕裂問(wèn)題。因此 React 對(duì)外提供了這樣一個(gè) API。

目前 React-Redux 8.0 已經(jīng)基于 useSyncExternalStore 實(shí)現(xiàn)。

useInsertionEffect

useInsertionEffect 的工作原理大致 useLayoutEffect 相同,只是此時(shí)無(wú)法訪問(wèn) DOM 節(jié)點(diǎn)的引用。

因此推薦的解決方案是使用這個(gè) Hook 來(lái)插入樣式表(或者如果你需要?jiǎng)h除它們,可以引用它們):

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
;
}

useId

useId 是一個(gè) API,用于在客戶端和服務(wù)器上生成唯一 ID,同時(shí)避免水合不匹配。使用示例:

function Checkbox() {
const id = useId();
return (




);
}

Concurrent(并發(fā)) 模式

Concurrent 模式是一組 React 的新功能,可幫助應(yīng)用保持響應(yīng),并根據(jù)用戶的設(shè)備性能和網(wǎng)速進(jìn)行適當(dāng)?shù)恼{(diào)整,該模式通過(guò)使渲染可中斷來(lái)修復(fù)阻塞渲染限制。在 Concurrent 模式中,React 可以同時(shí)更新多個(gè)狀態(tài)。

通常,當(dāng)我們更新 state 的時(shí)候,我們會(huì)期望這些變化立刻反映到屏幕上。我們期望應(yīng)用能夠持續(xù)響應(yīng)用戶的輸入,這是符合常理的。但是,有時(shí)我們會(huì)期望更新延遲響應(yīng)在屏幕上。在 React 中實(shí)現(xiàn)這個(gè)功能在之前是很難做到的。Concurrent 模式提供了一系列的新工具使之成為可能。

Transition

在 React 18 中,引入的一個(gè)新的 API startTransition,主要為了能在大量的任務(wù)下也能保持 UI 響應(yīng)。這個(gè)新的 API 可以通過(guò)將特定更新標(biāo)記為“過(guò)渡”來(lái)顯著改善用戶交互。

概覽:

import { startTransition } from'react';
// 緊急:顯示輸入的內(nèi)容
setInputValue(input);
// 標(biāo)記回調(diào)函數(shù)內(nèi)的更新為非緊急更新
startTransition(() => {
setSearchQuery(input);
});

簡(jiǎn)單來(lái)說(shuō),就是被 startTransition 回調(diào)包裹的 setState 觸發(fā)的渲染 被標(biāo)記為不緊急渲染,這些渲染可能被其他緊急渲染所搶占。

一般情況下,我們需要通知用戶后臺(tái)正在工作。為此提供了一個(gè)帶有 isPending 轉(zhuǎn)換標(biāo)志的 useTransition,React 將在狀態(tài)轉(zhuǎn)換期間提供視覺(jué)反饋,并在轉(zhuǎn)換發(fā)生時(shí)保持瀏覽器響應(yīng)。

import { useTransition } from'react';
const [isPending, startTransition] = useTransition();

該 isPending 值在轉(zhuǎn)換掛起時(shí)為 true,這時(shí)可以在頁(yè)面中放置一個(gè)加載器。

普通情況下:

使用 useTransition 表現(xiàn):

Demo 地址:??https://codesandbox.io/s/starttransition-demo-o59ld2。??

我們可以使用 startTransition 包裝任何要移至后臺(tái)的更新,通常,這些類型的更新分為兩類:

  • 渲染緩慢:這些更新需要時(shí)間,因?yàn)?React 需要執(zhí)行大量工作才能轉(zhuǎn)換 UI 以顯示結(jié)果。
  • 網(wǎng)絡(luò)慢:這些更新需要時(shí)間,因?yàn)?React 正在等待來(lái)自網(wǎng)絡(luò)的一些數(shù)據(jù)。這個(gè)方式與 Suspense 緊密集成。
  • 網(wǎng)絡(luò)慢場(chǎng)景:一個(gè)列表頁(yè),當(dāng)我們點(diǎn)擊 “下一頁(yè)”,現(xiàn)存的列表立刻消失了,然后我們看到整個(gè)頁(yè)面只有一個(gè)加載提示??梢哉f(shuō)這是一個(gè)“不受歡迎”的加載狀態(tài)。如果我們可以“跳過(guò)”這個(gè)過(guò)程,并且等到內(nèi)容加載后再過(guò)渡到新的頁(yè)面,效果會(huì)更好。

這里我們結(jié)合 Suspense 做加載邊界處理:

import React, { useState, useTransition, Suspense } from'react';
import { fetchMockData, MockItem } from'./utils';
import styles from'./DemoList.module.less';

const mockResource = fetchMockData(1);

exportdefaultfunction DemoList() {
const [resource, setResource] = useState(mockResource);
const [isPending, startTransition] = useTransition();

return (


className={styles.button}
type="button"
onClick={() =>
startTransition(() => {
setResource(fetchMockData(2));
})
}
>
下一頁(yè)

{isPending &&
加載中
}

);
}

function UserList({ resource }: UserListProps) {
const mockList = resource.read();
return (

{mockList.map((item) => (

{item.id}

{item.name}

{item.age} 歲


))}

);
}

結(jié)果展示:

Demo 地址:??https://codesandbox.io/s/usetransition-request-demo-wgedzw。??

把 Transition 融合到應(yīng)用的設(shè)計(jì)系統(tǒng)

useTransition 是非常常見(jiàn)的需求。幾乎所有可能導(dǎo)致組件掛起的點(diǎn)擊或交互操作都需要使用 useTransition,以避免意外隱藏用戶正在交互的內(nèi)容。

這可能會(huì)導(dǎo)致組件存在大量重復(fù)代碼。通常建議把 useTransition 融合到應(yīng)用的設(shè)計(jì)系統(tǒng)組件中。例如,我們可以把 useTransition 邏輯抽取到我們自己的組件:

function Button({ children, onClick }) {
const [startTransition, isPending] = useTransition();
function handleClick() {
startTransition(() => {
onClick();
});
}
return (

);
}

FAQ:useTransition 有個(gè)可選參數(shù),可以設(shè)定超時(shí)時(shí)間 timeoutMs,但目前的 TS 類型沒(méi)有開(kāi)放。

useDeferredValue

返回一個(gè)延遲響應(yīng)的值,這通常用于在具有基于用戶輸入立即渲染的內(nèi)容,以及需要等待數(shù)據(jù)獲取的內(nèi)容時(shí),保持接口的可響應(yīng)性。

import { useDeferredValue } from'react';
const deferredValue = useDeferredValue(value);

從介紹上來(lái)看 useDeferredValue 與 useTransition 是否感覺(jué)很相似呢?

  • 相同:useDeferredValue 本質(zhì)上和內(nèi)部實(shí)現(xiàn)與 useTransition 一樣都是標(biāo)記成了延遲更新任務(wù)。
  • 不同:useTransition 是把更新任務(wù)變成了延遲更新任務(wù),而 useDeferredValue 是產(chǎn)生一個(gè)新的值,這個(gè)值作為延時(shí)狀態(tài)。

那它和 debounce 有什么區(qū)別呢?

debounce 即 setTimeout 總是會(huì)有一個(gè)固定的延遲,而 useDeferredValue 的值只會(huì)在渲染耗費(fèi)的時(shí)間下滯后,在性能好的機(jī)器上,延遲會(huì)變少,反之則變長(zhǎng)。

結(jié)語(yǔ)

以上是本次 React 所升級(jí)的大致內(nèi)容,主要圍繞著并發(fā)模式而展開(kāi)。趕快提前準(zhǔn)備起來(lái)發(fā)布正式版后升級(jí)吧!


文章題目:一篇帶給你React18升級(jí)指南
瀏覽地址:http://www.5511xx.com/article/dhohdes.html