溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何解決第三方組件的Hooks報(bào)錯(cuò)問題

發(fā)布時(shí)間:2021-10-14 14:22:31 來(lái)源:億速云 閱讀:171 作者:iii 欄目:web開發(fā)

這篇文章主要介紹“如何解決第三方組件的Hooks報(bào)錯(cuò)問題”,在日常操作中,相信很多人在如何解決第三方組件的Hooks報(bào)錯(cuò)問題問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”如何解決第三方組件的Hooks報(bào)錯(cuò)問題”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

事發(fā)

某個(gè)需求需要引入一個(gè)第三方組件庫(kù)。

當(dāng)引入組件庫(kù)中的函數(shù)組件A后,React運(yùn)行時(shí)報(bào)錯(cuò):

  • "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文檔了解到,這是由于「錯(cuò)誤使用Hooks造成的」。

官網(wǎng)給出的可能的錯(cuò)誤原因有3種:

1.React和ReactDOM版本不匹配

需要v16.8以上版本的ReactDOM才支持Hooks。

我們項(xiàng)目使用的是v17.0.2,不屬于這個(gè)原因。

2.打破了Hooks的規(guī)則

Hooks只能在函數(shù)組件或自定義Hooks頂層調(diào)用。

翻看A組件源碼,報(bào)錯(cuò)的是一個(gè)頂層調(diào)用的useRef:

function A() {   // ...   var xxxRef = useRef(null);   // ... }

不屬于這個(gè)原因。

3.重復(fù)的React

載錄自React文檔:

  • 為了使 Hook 正常工作,你應(yīng)用代碼中的 react 依賴以及 react-dom 的 package 內(nèi)部使用的 react  依賴,必須解析為同一個(gè)模塊。

  • 如果這些 react 依賴解析為兩個(gè)不同的導(dǎo)出對(duì)象,你就會(huì)看到本警告。這可能發(fā)生在你意外地引入了兩個(gè) react 的 package 副本。

讀起來(lái)好繞,看起來(lái)這條的嫌疑最大。

定位問題

在報(bào)錯(cuò)的useRef中打上斷點(diǎn),發(fā)現(xiàn)其來(lái)自于:

http://localhost:8081/Users/項(xiàng)目目錄/node_modules/組件庫(kù)/node_modules/react/cjs/react.development.js

在項(xiàng)目里其他調(diào)用Hooks但是未報(bào)錯(cuò)的地方打上斷點(diǎn),發(fā)現(xiàn)資源來(lái)自于:

http://localhost:8081/Users/項(xiàng)目目錄/node_modules/react/cjs/react.development.js

報(bào)錯(cuò)的useRef和項(xiàng)目其他Hooks引用了不同的react.development.js。

翻看「組件庫(kù)」的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" },

這樣會(huì)在「組件庫(kù)」目錄的node_modules下創(chuàng)建這兩個(gè)依賴。

作為一個(gè)「組件庫(kù)」,這么做顯然是不合適的。

臨時(shí)解決

最好的做法是將這兩個(gè)依賴作為peerDependencies,即將其作為外部依賴。

這樣,當(dāng)我們引入「組件庫(kù)」時(shí),「組件庫(kù)」會(huì)使用我們項(xiàng)目中的react與react-dom,而不是自己安裝一份。

但是我沒有這個(gè)「組件庫(kù)」的權(quán)限,只能在自己項(xiàng)目中做文章。

在package.json文檔中提供了一個(gè)配置項(xiàng):resolutions,可以臨時(shí)解決這個(gè)問題。

resolutions允許你復(fù)寫一個(gè)在項(xiàng)目node_modules中被嵌套引用的包的版本。

在我們項(xiàng)目的package.json中作出如下修改:

// 項(xiàng)目package.json {   // ...   "resolutions": {     "react": "17.0.2",     "react-dom": "17.0.2"   },   // ... }

這樣,項(xiàng)目中用到的這兩個(gè)依賴都會(huì)使用resolutions中指定的版本。

不管是「組件庫(kù)」還是我們的項(xiàng)目代碼中的react與react-dom,都會(huì)指向同一個(gè)文件。

現(xiàn)在問題是臨時(shí)解決了,但是造成問題的原因是什么?

讓我們深入Hooks源碼內(nèi)部來(lái)尋找答案。

深入源碼

首先讓我們思考2個(gè)問題:

當(dāng)我們?cè)谝粋€(gè)Hooks內(nèi)部調(diào)用其他Hooks時(shí)會(huì)報(bào)開篇提到的錯(cuò)誤。

比如如下代碼就會(huì)報(bào)錯(cuò):

function App() {    useEffect(() => {     const a = useRef();   }, [])    // ... }

Hooks只是函數(shù),他如何感知到自己在另一個(gè)Hooks內(nèi)部執(zhí)行?

就如上例子,useRef如何感知到自己在useEffect的回調(diào)函數(shù)中執(zhí)行?

再看另一個(gè)問題,我們知道classComponent有componentDidMount與componentDidUpdate兩個(gè)生命周期函數(shù)區(qū)分mount時(shí)與update時(shí)。

那么Hooks作為函數(shù),怎么區(qū)分當(dāng)前是mount時(shí)還是update時(shí)?

顯然,Hooks源碼內(nèi)部存在一種機(jī)制,能夠感知當(dāng)前執(zhí)行的上下文環(huán)境。

漸入佳境

在瀏覽器環(huán)境,我們會(huì)引用react與reactDOM兩個(gè)包。

其中,在react包的代碼中存在一個(gè)變量ReactCurrentDispatcher。

他的current參數(shù)指向當(dāng)前正在使用的Hooks上下文:

var ReactCurrentDispatcher = {   /**    * @internal    * @type {ReactComponent}    */   current: null };

同時(shí),在reactDOM中,在程序運(yùn)行過程中,ReactCurrentDispatcher.current會(huì)根據(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時(shí),ReactCurrentDispatcher.current會(huì)指向HooksDispatcherOnMountInDEV。

當(dāng)處在DEV環(huán)境update時(shí),ReactCurrentDispatcher.current會(huì)指向HooksDispatcherOnUpdateInDEV。

再來(lái)看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; }
  • 可以看到,開篇的錯(cuò)誤正是由于dispatcher為null時(shí)拋出

這就是Hooks能區(qū)分mount與update的原因。

同理,DEV環(huán)境,當(dāng)一個(gè)Hooks在執(zhí)行時(shí),ReactCurrentDispatcher.current會(huì)指向引用 ——  InvalidNestedHooksDispatcherOnUpdateInDEV。

在這種情況下再調(diào)用的Hooks,比如如下useRef:

var InvalidNestedHooksDispatcherOnUpdateInDEV = {   // ...   useRef: function (initialValue) {     currentHookNameInDev = 'useRef';     warnInvalidHookAccess();     updateHookTypesDev();     return updateRef();   },   // ... }

內(nèi)部都會(huì)執(zhí)行warnInvalidHookAccess報(bào)錯(cuò),提示自己在別的Hooks內(nèi)執(zhí)行了。

真相大白

到這里我們終于知道開篇提到的問題發(fā)生的本質(zhì)原因:

  • 由于「組件庫(kù)」使用dependencies而不是peerDependencies,導(dǎo)致「組件庫(kù)」中引用的react與reactDOM是「組件庫(kù)」目錄node_modules下的文件。

  • 項(xiàng)目中使用的react與reactDOM是項(xiàng)目目錄node_modules下的文件。

  • 「組件庫(kù)」中react與項(xiàng)目目錄中react在運(yùn)行時(shí)分別初始化ReactCurrentDispatcher

  • 這兩個(gè)ReactCurrentDispatcher分別依賴對(duì)應(yīng)目錄的reactDOM

  • 我們?cè)陧?xiàng)目中執(zhí)行項(xiàng)目目錄下reactDOM的ReactDOM.render方法,他會(huì)隨著程序運(yùn)行改變項(xiàng)目目錄中react包下的ReactCurrentDispatcher.current的指向

  • 「組件庫(kù)」中的ReactCurrentDispatcher.current始終是null

  • 當(dāng)調(diào)用「組件庫(kù)」中的Hooks時(shí),由于ReactCurrentDispatcher.current始終是null導(dǎo)致報(bào)錯(cuò)

到此,關(guān)于“如何解決第三方組件的Hooks報(bào)錯(cuò)問題”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI