中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何解決第三方組件的Hooks報錯問題

發布時間:2021-10-14 14:22:31 來源:億速云 閱讀:184 作者:iii 欄目:web開發

這篇文章主要介紹“如何解決第三方組件的Hooks報錯問題”,在日常操作中,相信很多人在如何解決第三方組件的Hooks報錯問題問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何解決第三方組件的Hooks報錯問題”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

事發

某個需求需要引入一個第三方組件庫。

當引入組件庫中的函數組件A后,React運行時報錯:

  • "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造成的」。

官網給出的可能的錯誤原因有3種:

1.React和ReactDOM版本不匹配

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

我們項目使用的是v17.0.2,不屬于這個原因。

2.打破了Hooks的規則

Hooks只能在函數組件或自定義Hooks頂層調用。

翻看A組件源碼,報錯的是一個頂層調用的useRef:

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

不屬于這個原因。

3.重復的React

載錄自React文檔:

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

  • 如果這些 react 依賴解析為兩個不同的導出對象,你就會看到本警告。這可能發生在你意外地引入了兩個 react 的 package 副本。

讀起來好繞,看起來這條的嫌疑最大。

定位問題

在報錯的useRef中打上斷點,發現其來自于:

http://localhost:8081/Users/項目目錄/node_modules/組件庫/node_modules/react/cjs/react.development.js

在項目里其他調用Hooks但是未報錯的地方打上斷點,發現資源來自于:

http://localhost:8081/Users/項目目錄/node_modules/react/cjs/react.development.js

報錯的useRef和項目其他Hooks引用了不同的react.development.js。

翻看「組件庫」的package.json,發現他將react與react-dom作為dependencies安裝:

"dependencies": {   "react": "^16.13.1",   "@babel/runtime-corejs3": "^7.11.2",   "react-dom": "^16.13.1" },

這樣會在「組件庫」目錄的node_modules下創建這兩個依賴。

作為一個「組件庫」,這么做顯然是不合適的。

臨時解決

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

這樣,當我們引入「組件庫」時,「組件庫」會使用我們項目中的react與react-dom,而不是自己安裝一份。

但是我沒有這個「組件庫」的權限,只能在自己項目中做文章。

在package.json文檔中提供了一個配置項:resolutions,可以臨時解決這個問題。

resolutions允許你復寫一個在項目node_modules中被嵌套引用的包的版本。

在我們項目的package.json中作出如下修改:

// 項目package.json {   // ...   "resolutions": {     "react": "17.0.2",     "react-dom": "17.0.2"   },   // ... }

這樣,項目中用到的這兩個依賴都會使用resolutions中指定的版本。

不管是「組件庫」還是我們的項目代碼中的react與react-dom,都會指向同一個文件。

現在問題是臨時解決了,但是造成問題的原因是什么?

讓我們深入Hooks源碼內部來尋找答案。

深入源碼

首先讓我們思考2個問題:

當我們在一個Hooks內部調用其他Hooks時會報開篇提到的錯誤。

比如如下代碼就會報錯:

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

Hooks只是函數,他如何感知到自己在另一個Hooks內部執行?

就如上例子,useRef如何感知到自己在useEffect的回調函數中執行?

再看另一個問題,我們知道classComponent有componentDidMount與componentDidUpdate兩個生命周期函數區分mount時與update時。

那么Hooks作為函數,怎么區分當前是mount時還是update時?

顯然,Hooks源碼內部存在一種機制,能夠感知當前執行的上下文環境。

漸入佳境

在瀏覽器環境,我們會引用react與reactDOM兩個包。

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

他的current參數指向當前正在使用的Hooks上下文:

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

同時,在reactDOM中,在程序運行過程中,ReactCurrentDispatcher.current會根據當前上下文環境指向不同引用。

比如:

var HooksDispatcherOnMountInDEV = {   useState: function() { // ... },   useEffect: function() { // ... },   useRef: function() { // ... },   // ... } var HooksDispatcherOnUpdateInDEV = {   useState: function() { // ... },   useEffect: function() { // ... },   useRef: function() { // ... },   // ... } // ...

當處在DEV環境mount時,ReactCurrentDispatcher.current會指向HooksDispatcherOnMountInDEV。

當處在DEV環境update時,ReactCurrentDispatcher.current會指向HooksDispatcherOnUpdateInDEV。

再來看useRef的定義:

function useRef(initialValue) {   var dispatcher = resolveDispatcher();   return dispatcher.useRef(initialValue); }

內部調用的是dispatcher.useRef。

dispatcher即ReactCurrentDispatcher.current。

function resolveDispatcher() {   var dispatcher = ReactCurrentDispatcher.current;    if (!(dispatcher !== null)) {     {       throw Error( "Invalid hook call. ..." );     }   }    return dispatcher; }
  • 可以看到,開篇的錯誤正是由于dispatcher為null時拋出

這就是Hooks能區分mount與update的原因。

同理,DEV環境,當一個Hooks在執行時,ReactCurrentDispatcher.current會指向引用 ——  InvalidNestedHooksDispatcherOnUpdateInDEV。

在這種情況下再調用的Hooks,比如如下useRef:

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

內部都會執行warnInvalidHookAccess報錯,提示自己在別的Hooks內執行了。

真相大白

到這里我們終于知道開篇提到的問題發生的本質原因:

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

  • 項目中使用的react與reactDOM是項目目錄node_modules下的文件。

  • 「組件庫」中react與項目目錄中react在運行時分別初始化ReactCurrentDispatcher

  • 這兩個ReactCurrentDispatcher分別依賴對應目錄的reactDOM

  • 我們在項目中執行項目目錄下reactDOM的ReactDOM.render方法,他會隨著程序運行改變項目目錄中react包下的ReactCurrentDispatcher.current的指向

  • 「組件庫」中的ReactCurrentDispatcher.current始終是null

  • 當調用「組件庫」中的Hooks時,由于ReactCurrentDispatcher.current始終是null導致報錯

到此,關于“如何解決第三方組件的Hooks報錯問題”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

滦南县| 东丰县| 旺苍县| 深州市| 舒兰市| 黑龙江省| 汪清县| 潞城市| 新竹县| 钟山县| 遂溪县| 英吉沙县| 天全县| 佛学| 荆州市| 宜兰县| 辽宁省| 鹤壁市| 西林县| 商南县| 察雅县| 安平县| 中超| 时尚| 永平县| 毕节市| 新田县| 石首市| 阜城县| 德钦县| 无为县| 香河县| 祥云县| 泰来县| 永靖县| 乐清市| 葵青区| 通许县| 横山县| 山西省| 淮滨县|