這篇文章主要介紹“如何解決第三方組件的Hooks報錯問題”,在日常操作中,相信很多人在如何解決第三方組件的Hooks報錯問題問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何解決第三方組件的Hooks報錯問題”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
創(chuàng)新互聯(lián)是一家專注于成都網(wǎng)站設(shè)計、網(wǎng)站制作、外貿(mào)營銷網(wǎng)站建設(shè)與策劃設(shè)計,陜州網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)十多年,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:陜州等地區(qū)。陜州做網(wǎng)站價格咨詢:13518219792
某個需求需要引入一個第三方組件庫。
當(dāng)引入組件庫中的函數(shù)組件A后,React運(yùn)行時報錯:
"Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons...
從React文檔了解到,這是由于「錯誤使用Hooks造成的」。
官網(wǎng)給出的可能的錯誤原因有3種:
1.React和ReactDOM版本不匹配
需要v16.8以上版本的ReactDOM才支持Hooks。
我們項目使用的是v17.0.2,不屬于這個原因。
2.打破了Hooks的規(guī)則
Hooks只能在函數(shù)組件或自定義Hooks頂層調(diào)用。
翻看A組件源碼,報錯的是一個頂層調(diào)用的useRef:
function A() { // ... var xxxRef = useRef(null); // ... }
不屬于這個原因。
3.重復(fù)的React
載錄自React文檔:
為了使 Hook 正常工作,你應(yīng)用代碼中的 react 依賴以及 react-dom 的 package 內(nèi)部使用的 react 依賴,必須解析為同一個模塊。
如果這些 react 依賴解析為兩個不同的導(dǎo)出對象,你就會看到本警告。這可能發(fā)生在你意外地引入了兩個 react 的 package 副本。
讀起來好繞,看起來這條的嫌疑最大。
在報錯的useRef中打上斷點,發(fā)現(xiàn)其來自于:
http://localhost:8081/Users/項目目錄/node_modules/組件庫/node_modules/react/cjs/react.development.js
在項目里其他調(diào)用Hooks但是未報錯的地方打上斷點,發(fā)現(xiàn)資源來自于:
http://localhost:8081/Users/項目目錄/node_modules/react/cjs/react.development.js
報錯的useRef和項目其他Hooks引用了不同的react.development.js。
翻看「組件庫」的package.json,發(fā)現(xiàn)他將react與react-dom作為dependencies安裝:
"dependencies": { "react": "^16.13.1", "@babel/runtime-corejs3": "^7.11.2", "react-dom": "^16.13.1" },
這樣會在「組件庫」目錄的node_modules下創(chuàng)建這兩個依賴。
作為一個「組件庫」,這么做顯然是不合適的。
最好的做法是將這兩個依賴作為peerDependencies,即將其作為外部依賴。
這樣,當(dāng)我們引入「組件庫」時,「組件庫」會使用我們項目中的react與react-dom,而不是自己安裝一份。
但是我沒有這個「組件庫」的權(quán)限,只能在自己項目中做文章。
在package.json文檔中提供了一個配置項:resolutions,可以臨時解決這個問題。
resolutions允許你復(fù)寫一個在項目node_modules中被嵌套引用的包的版本。
在我們項目的package.json中作出如下修改:
// 項目package.json { // ... "resolutions": { "react": "17.0.2", "react-dom": "17.0.2" }, // ... }
這樣,項目中用到的這兩個依賴都會使用resolutions中指定的版本。
不管是「組件庫」還是我們的項目代碼中的react與react-dom,都會指向同一個文件。
現(xiàn)在問題是臨時解決了,但是造成問題的原因是什么?
讓我們深入Hooks源碼內(nèi)部來尋找答案。
首先讓我們思考2個問題:
當(dāng)我們在一個Hooks內(nèi)部調(diào)用其他Hooks時會報開篇提到的錯誤。
比如如下代碼就會報錯:
function App() { useEffect(() => { const a = useRef(); }, []) // ... }
Hooks只是函數(shù),他如何感知到自己在另一個Hooks內(nèi)部執(zhí)行?
就如上例子,useRef如何感知到自己在useEffect的回調(diào)函數(shù)中執(zhí)行?
再看另一個問題,我們知道classComponent有componentDidMount與componentDidUpdate兩個生命周期函數(shù)區(qū)分mount時與update時。
那么Hooks作為函數(shù),怎么區(qū)分當(dāng)前是mount時還是update時?
顯然,Hooks源碼內(nèi)部存在一種機(jī)制,能夠感知當(dāng)前執(zhí)行的上下文環(huán)境。
在瀏覽器環(huán)境,我們會引用react與reactDOM兩個包。
其中,在react包的代碼中存在一個變量ReactCurrentDispatcher。
他的current參數(shù)指向當(dāng)前正在使用的Hooks上下文:
var ReactCurrentDispatcher = { /** * @internal * @type {ReactComponent} */ current: null };
同時,在reactDOM中,在程序運(yùn)行過程中,ReactCurrentDispatcher.current會根據(jù)當(dāng)前上下文環(huán)境指向不同引用。
比如:
var HooksDispatcherOnMountInDEV = { useState: function() { // ... }, useEffect: function() { // ... }, useRef: function() { // ... }, // ... } var HooksDispatcherOnUpdateInDEV = { useState: function() { // ... }, useEffect: function() { // ... }, useRef: function() { // ... }, // ... } // ...
當(dāng)處在DEV環(huán)境mount時,ReactCurrentDispatcher.current會指向HooksDispatcherOnMountInDEV。
當(dāng)處在DEV環(huán)境update時,ReactCurrentDispatcher.current會指向HooksDispatcherOnUpdateInDEV。
再來看useRef的定義:
function useRef(initialValue) { var dispatcher = resolveDispatcher(); return dispatcher.useRef(initialValue); }
內(nèi)部調(diào)用的是dispatcher.useRef。
dispatcher即ReactCurrentDispatcher.current。
function resolveDispatcher() { var dispatcher = ReactCurrentDispatcher.current; if (!(dispatcher !== null)) { { throw Error( "Invalid hook call. ..." ); } } return dispatcher; }
可以看到,開篇的錯誤正是由于dispatcher為null時拋出
這就是Hooks能區(qū)分mount與update的原因。
同理,DEV環(huán)境,當(dāng)一個Hooks在執(zhí)行時,ReactCurrentDispatcher.current會指向引用 —— InvalidNestedHooksDispatcherOnUpdateInDEV。
在這種情況下再調(diào)用的Hooks,比如如下useRef:
var InvalidNestedHooksDispatcherOnUpdateInDEV = { // ... useRef: function (initialValue) { currentHookNameInDev = 'useRef'; warnInvalidHookAccess(); updateHookTypesDev(); return updateRef(); }, // ... }
內(nèi)部都會執(zhí)行warnInvalidHookAccess報錯,提示自己在別的Hooks內(nèi)執(zhí)行了。
到這里我們終于知道開篇提到的問題發(fā)生的本質(zhì)原因:
由于「組件庫」使用dependencies而不是peerDependencies,導(dǎo)致「組件庫」中引用的react與reactDOM是「組件庫」目錄node_modules下的文件。
項目中使用的react與reactDOM是項目目錄node_modules下的文件。
「組件庫」中react與項目目錄中react在運(yùn)行時分別初始化ReactCurrentDispatcher
這兩個ReactCurrentDispatcher分別依賴對應(yīng)目錄的reactDOM
我們在項目中執(zhí)行項目目錄下reactDOM的ReactDOM.render方法,他會隨著程序運(yùn)行改變項目目錄中react包下的ReactCurrentDispatcher.current的指向
「組件庫」中的ReactCurrentDispatcher.current始終是null
當(dāng)調(diào)用「組件庫」中的Hooks時,由于ReactCurrentDispatcher.current始終是null導(dǎo)致報錯
到此,關(guān)于“如何解決第三方組件的Hooks報錯問題”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
當(dāng)前題目:如何解決第三方組件的Hooks報錯問題
路徑分享:http://chinadenli.net/article30/jioeso.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)、響應(yīng)式網(wǎng)站、標(biāo)簽優(yōu)化、商城網(wǎng)站、網(wǎng)站制作、App開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)