React 19 升級(jí)官方指南:值得注意的變化
共 6432字,需瀏覽 13分鐘
·
2024-05-10 08:00
本文適合對(duì)React 19 beta感興趣的小伙伴閱讀。
歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進(jìn)階~
前言
在本文中,我們將科普將庫(kù)升級(jí)到 React 19 beta 值得注意的變化:
-
新的棄用功能 -
顯著更改 -
TS 更改
新的已棄用功能
1. 已棄用:element.ref
React 19 支持 ref 作為 prop,因此我們棄用 element.ref 來(lái)代替 element.props.ref。
2. 已棄用:react-test-renderer
我們棄用了 react-test-renderer,因?yàn)樗鼘?shí)現(xiàn)了自己的渲染器環(huán)境,這不匹配用戶使用的環(huán)境,促進(jìn)了測(cè)試實(shí)現(xiàn)細(xì)節(jié),且依賴 React 內(nèi)部的內(nèi)省。
測(cè)試渲染器是在有 React Testing Library 等更可行的測(cè)試策略之前創(chuàng)建的,我們現(xiàn)在建議使用現(xiàn)代測(cè)試庫(kù)取而代之。
在 React 19 中,react-test-renderer 打印了棄用警告,且已切換到并發(fā)渲染。我們建議將你的測(cè)試遷移到 @testing-library/react 或 @testing-library/react-native,獲取現(xiàn)代化且良好支持的測(cè)試體驗(yàn)。
值得注意的變化
1. 嚴(yán)格模式更改
在開發(fā)中以嚴(yán)格模式二次渲染時(shí),useMemo 和 useCallback 將在二次渲染期間復(fù)用首次渲染的記憶化結(jié)果。兼容嚴(yán)格模式的組件不應(yīng)注意到這個(gè)行為差異。
與所有嚴(yán)格模式行為一樣,這些功能旨在在開發(fā)過(guò)程中主動(dòng)發(fā)現(xiàn)組件中的 bug,這樣你可以在將它們交付生產(chǎn)之前修復(fù)它們。
舉個(gè)栗子,在開發(fā)過(guò)程中,嚴(yán)格模式將在初始掛載時(shí)兩次執(zhí)行 ref 回調(diào)函數(shù),模擬掛載的組件被 Suspense 后備方案替換時(shí)發(fā)生的情況。
2. UMD 構(gòu)建已刪除
UMD 過(guò)去被廣泛使用,作為一種無(wú)需構(gòu)建步驟即可加載 React 的便捷方式?,F(xiàn)在,有一些現(xiàn)代替代方案可以將模塊作為腳本加載到 HTML 文檔中。
從 React 19 開始,React 將不再生成 UMD 構(gòu)建,降低其測(cè)試和發(fā)布過(guò)程的復(fù)雜性。
要使用 script 標(biāo)記加載 React 19,我們建議使用基于 ESM 的 CDN,比如 esm.sh。
<script type="module">
import React from "https://esm.sh/react@19/?dev"
import ReactDOMClient
from "https://esm.sh/react-dom@19/client?dev"
</script>
3. 依賴 React 內(nèi)部的庫(kù)可能會(huì)阻礙升級(jí)
React 19 包含了對(duì) React 內(nèi)部結(jié)構(gòu)的更改,這些更改可能會(huì)影響那些忽略我們不使用 SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED 等內(nèi)部結(jié)構(gòu)的請(qǐng)求的庫(kù)。為了改良 React 19,這些更改勢(shì)在必行,且不會(huì)破壞遵循我們指南的庫(kù)。
根據(jù)我們的版本控制政策,這些更新未列為破壞性更改,且我們不包含有關(guān)如何升級(jí)它們的文檔。建議刪除任何依賴內(nèi)部的代碼。
為了反映使用內(nèi)部結(jié)構(gòu)的影響,我們將 SECRET_INTERNALS 后綴重命名為:_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE。
將來(lái),我們將更積極地阻止從 React 訪問(wèn)內(nèi)部組件,阻止使用并確保用戶不會(huì)被阻礙升級(jí)。
TS 更改
1. 刪除了已棄用的 TS 類型
我們根據(jù) React 19 中刪除的 API 清理了 TS 類型。一些刪除的類型已移至更相關(guān)的包中,而其他類型則不再需要描述 React 的行為。
我們發(fā)布了 types-react-codemod 來(lái)遷移大多數(shù)類型相關(guān)的破壞性更改:
npx types-react-codemod@latest preset-19
./path-to-app
如果你對(duì) element.props 有很多不健全的訪問(wèn)權(quán)限,你可以運(yùn)行這個(gè)額外的 codemod:
npx types-react-codemod@latest
react-element-default-any-props
./path-to-your-react-ts-files
2. 需要的 ref 清理函數(shù)
此更改包含在 react-19 codemod 預(yù)設(shè)中,作為 no-implicit-ref-callback-return。
由于 ref 清理函數(shù)的引入,從 ref 回調(diào)函數(shù)返回任何其他內(nèi)容現(xiàn)在都將被 TS 拒絕。解決方法通常是停止使用隱式返回:
- <div ref={current => (instance = current)} />
+ <div ref={current => {instance = current}} />
原始代碼返回 HTMLDivElement 的實(shí)例,TS 不知道這是否應(yīng)該是一個(gè)清理函數(shù)。
3. useRef 需要一個(gè)參數(shù)
此更改包含在 react-19 codemod 預(yù)設(shè)中,作為 refobject-defaults。
長(zhǎng)期以來(lái)對(duì) TS 和 React 工作方式的抱怨是 useRef。我們更改了類型,因此 useRef 現(xiàn)在需要參數(shù)。這顯著簡(jiǎn)化了其類型簽名?,F(xiàn)在它的行為更像 createContext。
// @ts-expect-error:期望一個(gè)參數(shù)但沒(méi)有
useRef()
// 類型合法
useRef(undefined)
// @ts-expect-error:期望一個(gè)參數(shù)但沒(méi)有
createContext()
// 類型合法
createContext(undefined)
現(xiàn)在這也意味著所有 ref 都是可變的。你將不再遭遇無(wú)法更改 ref 的問(wèn)題,因?yàn)槟闶褂?nbsp;null 初始化了它:
const ref = useRef<number>(null)
// 無(wú)法賦值給 current,
// 因?yàn)樗且粋€(gè)只讀屬性
ref.current = 1
MutableRef 現(xiàn)已棄用,取而代之的是 RefObject 類型,useRef 將始終返回:
interface RefObject<T> {
current: T
}
declare function useRef<T>: RefObject<T>
useRef 仍具有自動(dòng)返回 RefObject<T | null> 的 useRef<T>(null) 便捷重載。為了簡(jiǎn)化由于 useRef 所需參數(shù)而導(dǎo)致的遷移,添加了 useRef(undefined) 的便捷重載,它會(huì)自動(dòng)返回 RefObject<T | undefined>。
4. 對(duì) ReactElement TS 類型的更改
此更改包含在 react-element-default-any-props codemod 中。
如果元素類型為 ReactElement,React 元素的 props 現(xiàn)在默認(rèn)為 unknown 而不是 any。如果你將類型參數(shù)傳遞給 ReactElement,這不會(huì)影響到你:
type E = ReactElement<{id: string}>['props']
// ^? { id: string }
但如果你依賴默認(rèn)值,你現(xiàn)在必須處理 unknown:
type Example = ReactElement['props']
// ^? 切換前是 any,現(xiàn)在是 unknown
當(dāng)且僅當(dāng)你有大量依賴元素屬性的不健全訪問(wèn)的過(guò)時(shí)代碼時(shí)才需要它。元素內(nèi)省僅作為逃生通道存在,你應(yīng)該通過(guò)顯式 any 明確表明你的 props 訪問(wèn)不健全。
5. TS 中的 JSX 命名空間
此更改包含在 react-19 codemod 預(yù)設(shè)中,作為 scoped-jsx。
一個(gè)長(zhǎng)期的請(qǐng)求是從我們的類型中刪除全局 JSX 命名空間,以支持 React.JSX。這有助于防止全局類型污染,防止利用 JSX 的不同 UI 庫(kù)之間發(fā)生沖突。
現(xiàn)在,你需要將 JSX 命名空間的模塊擴(kuò)展包裝在 declare module ”...” 中:
// global.d.ts
+ declare module "react" {
namespace JSX {
interface IntrinsicElements {
"my-element": {
myElementProps: string;
};
}
}
+ }
確切的模塊說(shuō)明符取決于你在 tsconfig.json 的 compilerOptions 中指定的 JSX 運(yùn)行時(shí):
-
對(duì)于 "jsx": "react-jsx",它將是react/jsx-runtime。 -
對(duì)于 "jsx": "react-jsxdev",它將是react/jsx-dev-runtime。 -
對(duì)于 "jsx": "react"和"jsx": "preserve",它將是react。
6. 更好的 useReducer typings
useReducer 現(xiàn)在改進(jìn)了類型推斷。
然而,這需要一個(gè)破壞性更改,其中 useReducer 不接受完整的 reducer 類型作為類型參數(shù),而是要么不需要任何類型且依賴上下文類型,要么需要 state 和 action 類型。
新的最佳實(shí)踐是不要將類型參數(shù)傳遞給 useReducer。
- useReducer<
- React.Reducer<State, Action>>(reducer)
+ useReducer(reducer)
這在極端情況下可能行不通,你可以通過(guò)在元組中傳入 Action 來(lái)顯式鍵入 state 和 action:
- useReducer<
- React.Reducer<State, Action>>(reducer)
+ useReducer<State, [Action]>(reducer)
如果你內(nèi)聯(lián)定義 reducer,我們鼓勵(lì)改為注解函數(shù)參數(shù):
- useReducer<React.Reducer<State, Action>>(
- (state, action) => state)
+ useReducer(
+ (state: State, action: Action) => state)
如果你將 reducer 移到 useReducer 調(diào)用之外,你也必須執(zhí)行以下操作:
const reducer = (
state: State, action: Action) => state
參考文獻(xiàn)
-
React:https://react.dev -
Upgrade Guide:https://react.dev/blog/2024/04/25/react-19-upgrade-guide -
facebook/react:https://github.com/facebook/react
最后
到這里我們知道了升級(jí)react19要注意的一些事項(xiàng),感興趣的伙伴可以前往官方文檔進(jìn)一步查看。
關(guān)注我,一起攜手進(jìn)階
歡迎關(guān)注前端早茶,與廣東靚仔攜手共同進(jìn)階~
