React工作流程及Error Boundaries實(shí)現(xiàn)過(guò)程講解
這里簡(jiǎn)單講解下React工作流程,后文有用。分為三步:
觸發(fā)更新
- render階段:計(jì)算更新會(huì)造成的副作用
- commit階段:在宿主環(huán)境執(zhí)行副作用
副作用有很多,比如:
- 插入DOM節(jié)點(diǎn)
- 執(zhí)行useEffect回調(diào)
好了,讓我們進(jìn)入主題。
什么是Error Boundaries
React提供了兩個(gè)與錯(cuò)誤處理相關(guān)的API:
- getDerivedStateFromError:靜態(tài)方法,當(dāng)錯(cuò)誤發(fā)生后提供一個(gè)機(jī)會(huì)渲染fallback
- UIcomponentDidCatch:組件實(shí)例方法,當(dāng)錯(cuò)誤發(fā)生后提供一個(gè)機(jī)會(huì)記錄錯(cuò)誤信息
使用了這兩個(gè)API的ClassComponent通常被稱(chēng)為Error Boundaries(錯(cuò)誤邊界)。
在Error Boundaries的子孫組件中發(fā)生的所有React工作流程內(nèi)的錯(cuò)誤都會(huì)被Error Boundaries捕獲。
通過(guò)開(kāi)篇的介紹可以知道,React工作流程指:
render階段
commit階段
考慮如下代碼:
class ErrorBoundary extends Component { componentDidCatch(e) { console.warn(“發(fā)生錯(cuò)誤”, e); } render() { return <div>{this.props.children}</div>; } } const App = () => ( <ErrorBoundary> <A><B/></A> <C/> <ErrorBoundary> )
A、B、C作為ErrorBoundary的子孫組件,當(dāng)發(fā)生React工作流程內(nèi)的錯(cuò)誤,都會(huì)被ErrorBoundary中的componentDidCatch方法捕獲。
步驟1:捕獲錯(cuò)誤
首先來(lái)看工作流程中的錯(cuò)誤都是何時(shí)被捕獲的。
render階段的核心代碼如下,發(fā)生的錯(cuò)誤會(huì)被handleError處理:
do { try { // 對(duì)于并發(fā)更新則是workLoopConcurrent workLoopSync(); break; } catch (thrownValue) { handleError(root, thrownValue); } } while (true);
commit階段包含很多工作,比如:
- componentDidMount/Update執(zhí)行
- 綁定/解綁ref
- useEffect/useLayoutEffect callback與destroy執(zhí)行
這些工作會(huì)以如下形式執(zhí)行,發(fā)生的錯(cuò)誤被captureCommitPhaseError處理:
try { // …執(zhí)行某項(xiàng)工作 } catch (error) { captureCommitPhaseError(fiber, fiber.return, error); }
步驟2:構(gòu)造callback
可以發(fā)現(xiàn),即使沒(méi)有Error Boundaries,工作流程中的錯(cuò)誤已經(jīng)被React捕獲了。而正確的邏輯應(yīng)該是:
- 如果存在Error Boundaries,執(zhí)行對(duì)應(yīng)API
- 拋出React的提示信息
- 如果不存在Error Boundaries,拋出未捕獲的錯(cuò)誤
所以,不管是handleError還是captureCommitPhaseError,都會(huì)從發(fā)生錯(cuò)誤的節(jié)點(diǎn)的父節(jié)點(diǎn)開(kāi)始,逐層向上遍歷,尋找最近的Error Boundaries。
一旦找到,就會(huì)構(gòu)造:
- 用于執(zhí)行Error Boundaries API的callback
- 用于拋出React提示信息的callback
// ...為了可讀性,邏輯有刪減 function createClassErrorUpdate() { if (typeof getDerivedStateFromError === 'function') { // 用于執(zhí)行g(shù)etDerivedStateFromError的callback update.payload = () => { return getDerivedStateFromError(error); }; // 用于拋出React提示信息的callback update.callback = () => { logCapturedError(fiber, errorInfo); }; } if (inst !== null && typeof inst.componentDidCatch === 'function') { // 用于執(zhí)行componentDidCatch的callback update.callback = function callback() { this.componentDidCatch(error); }; } return update; }
如果沒(méi)有找到Error Boundaries,繼續(xù)向上遍歷直到根節(jié)點(diǎn)。
此時(shí)會(huì)構(gòu)造:
用于拋出未捕獲錯(cuò)誤的callback用于拋出React提示信息的callback
// ...為了可讀性,邏輯有刪減 funffction createRootErrorUpdate() { // 用于拋出“未捕獲的錯(cuò)誤”及“React的提示信息”的callback update.callback = () => { onUncaughtError(error); logCapturedError(fiber, errorInfo); }; return update; }
執(zhí)行callback
構(gòu)造好的callback在什么時(shí)候執(zhí)行呢?
在React中有兩個(gè)執(zhí)行用戶(hù)自定義callback的API:
對(duì)于ClassComponent, this.setState(newState, callback)中newState和callback參數(shù)都能傳遞Function作為callback
所以,對(duì)于Error Boundaries,相當(dāng)于主動(dòng)觸發(fā)了一次更新:
this.setState(() => { // 用于執(zhí)行g(shù)etDerivedStateFromError的callback }, () => { // 用于執(zhí)行componentDidCatch的callback // 以及 用于拋出React提示信息的callback })
對(duì)于根節(jié)點(diǎn),執(zhí)行ReactDOM.render(element, container, callback)中callback參數(shù)能傳遞Function作為callback
所以,對(duì)于沒(méi)有Error Boundaries的情況,相當(dāng)于主動(dòng)執(zhí)行了如下函數(shù):
ReactDOM.render(element, container, () => { // 用于拋出“未捕獲的錯(cuò)誤”及“React的提示信息”的callback })
所以,Error Boundaries的實(shí)現(xiàn)可以看作是:React利用已有API實(shí)現(xiàn)的新功能。
總結(jié)
經(jīng)常有人問(wèn):為什么Hooks沒(méi)有Error Boundaries?
可以看到,Error Boundaries的實(shí)現(xiàn)借助了this.setState可以傳遞callback的特性,useState暫時(shí)無(wú)法完全對(duì)標(biāo)。
以上就是React工作流程及Error Boundaries實(shí)現(xiàn)過(guò)程講解的詳細(xì)內(nèi)容,更多關(guān)于React工作流程Error Boundaries實(shí)現(xiàn)過(guò)程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳細(xì)談?wù)凴eact中setState是一個(gè)宏任務(wù)還是微任務(wù)
學(xué)過(guò)react的人都知道,setState在react里是一個(gè)很重要的方法,使用它可以更新我們數(shù)據(jù)的狀態(tài),下面這篇文章主要給大家介紹了關(guān)于React中setState是一個(gè)宏任務(wù)還是微任務(wù)的相關(guān)資料,需要的朋友可以參考下2021-09-09React?程序設(shè)計(jì)簡(jiǎn)單的輕量級(jí)自動(dòng)完成搜索框應(yīng)用
這篇文章主要為大家介紹了React?程序設(shè)計(jì)簡(jiǎn)單的輕量級(jí)自動(dòng)完成搜索框應(yīng)用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10一文詳解React Redux設(shè)計(jì)思想與工作原理
最近看項(xiàng)目中使用了?Redux,?便嘗試了解一波?Redux?的設(shè)計(jì)思想與工作原理,所以本文詳細(xì)的給大家介紹了Redux設(shè)計(jì)思想與工作原理,需要的朋友可以參考下2023-09-09react-native使用react-navigation進(jìn)行頁(yè)面跳轉(zhuǎn)導(dǎo)航的示例
本篇文章主要介紹了react-native使用react-navigation進(jìn)行頁(yè)面跳轉(zhuǎn)導(dǎo)航的示例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-09-09react?+?vite?+?ts項(xiàng)目中優(yōu)雅使用.svg文件
這篇文章主要為大家介紹了react?+?vite?+?ts項(xiàng)目中優(yōu)雅使用.svg文件,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08react循環(huán)數(shù)據(jù)(列表)的實(shí)現(xiàn)
這篇文章主要介紹了react循環(huán)數(shù)據(jù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04React?Native?的動(dòng)態(tài)列表方案探索詳解
這篇文章主要為大家介紹了React?Native?的動(dòng)態(tài)列表方案探索示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09