詳解React Hooks是如何工作的
1. React Hooks VS 純函數(shù)
React Hook 說白了就是 React V18.6 新增的一些 API,API的本質(zhì)就是提供某種功能的函數(shù)接口。因此,React Hooks 就是一些函數(shù),但是 React Hooks 不是純函數(shù)。
什么是純函數(shù)呢?就是此函數(shù)在相同的輸入值時(shí),需產(chǎn)生相同的輸出,并且此函數(shù)不能影響到外面的數(shù)據(jù)。
簡單理解就是函數(shù)里面不能用到在外面定義的變量,因?yàn)槿绻玫搅送饷娑x的變量,當(dāng)外面的變量改變時(shí)會(huì)影響函數(shù)內(nèi)部的計(jì)算,函數(shù)也會(huì)影響到外面的變量。
對于 React Hooks 提供的函數(shù) API,恰恰就不是純函數(shù)。
來看一個(gè) useState 的使用語句 const [count, setCount] = useState(0),使用 useState 函數(shù)得到的結(jié)果并不是全都一樣的,因?yàn)槿绻?useState(0) 每次得到的結(jié)果都是一樣的,那 count 值就永遠(yuǎn)不會(huì)改變了,那 count 所在的頁面就永遠(yuǎn)不會(huì)改變,和我們看到的結(jié)果就不一樣了。由此可知,React Hooks 都不是純函數(shù),也就是說 Hooks 用到了函數(shù)外的變量。
那么是什么特性讓 React Hooks 一定不能是純函數(shù)呢?實(shí)際上是 React 框架和函數(shù)組件本身決定的。我們知道,React 頁面渲染的原理就是通過每次 render 得到新的虛擬 DOM ,然后進(jìn)行 DOM Diff 來渲染頁面。而 React 的函數(shù)組件是通過執(zhí)行整個(gè)函數(shù)得到一個(gè)虛擬 DOM。因此在每次頁面渲染 render 時(shí),在函數(shù)組件內(nèi)部的所有語句都會(huì)重新執(zhí)行一次。如果在函數(shù)組件內(nèi)部使用的 React Hooks 是純函數(shù)的話,就不會(huì)在每次渲染后得到不同的虛擬 DOM 了。
React 規(guī)定: 所有 React 組件都必須是純函數(shù),并禁止修改其自身 props 。
因此在 React V16.8 之前 React Hooks 還沒出來的時(shí)候,函數(shù)組件因?yàn)槭羌兒瘮?shù),只能返回一個(gè)固定的虛擬 DOM,不能包含狀態(tài),也不支持生命周期方法。因此,當(dāng)時(shí)僅僅是支持函數(shù)組件,但函數(shù)組件相比于類組件限制太多,函數(shù)組件無法取代類組件,也沒類組件好用。
React 希望組件是簡單的而不是復(fù)雜的,React 認(rèn)為組件的最佳寫法應(yīng)該是函數(shù),而不是類。因此 React 就新增了 React Hooks,Hook 就是鉤子的意思,是 React 提供給函數(shù)組件在需要外部功能和數(shù)據(jù)狀態(tài)時(shí)將其 “鉤” 進(jìn)去,從而完善函數(shù)組件,使其能完全代替類組件。
React 的函數(shù)組件只能是純函數(shù),那么每次事件發(fā)生時(shí)重新 render 函數(shù)組件時(shí)得到不同的虛擬 DOM 的事就完全交給了 React Hooks,那么 React Hooks 是如何做到的呢?下面就手動(dòng)實(shí)現(xiàn)一個(gè) useState,useState 的具體細(xì)節(jié)肯定不是這樣的,但原理和思路是一樣的。
2. 簡單 myUseState
React.useState 的第一次執(zhí)行是將初始值賦予給一個(gè) _state,之后的每次重新 render 時(shí)就是讀取 _state 的值。[state, setState] 中的 setState 做的事就是改變 _state 的值,然后重新渲染頁面。
根據(jù)這個(gè)原理實(shí)現(xiàn) myUseState 函數(shù)如下:
import React from 'react'; import ReactDOM from 'react-dom'; let _state function myUseState(initialValue){ if(_state === undefined){ _state = initialValue } const setState = (newValue)=>{ _state = newValue render() } return [_state, setState] } function render(){ ReactDOM.render(<App/>,document.getElementById('root')); } function App(){ const [n, setN] = myUseState(0) return ( <div> n: {n} <button onClick={() => setN(n+1)}>+1</button> </div> ) } ReactDOM.render(<App/>,document.getElementById('root'));
3. 改進(jìn) myUseState
上述實(shí)現(xiàn)的 myUseState 存在 bug,當(dāng)在函數(shù)組件內(nèi)用到兩次 myUseState 時(shí)就會(huì)出現(xiàn)問題了,二者共用一個(gè) _state 會(huì)出現(xiàn)混亂。
因此需要將上述實(shí)現(xiàn)進(jìn)行改進(jìn),改進(jìn)的思路就是將 _state 定義為一個(gè)數(shù)據(jù)或者是對象,由于我們在函數(shù)使用時(shí)只傳了一個(gè)數(shù)值,無法確定鍵值,因此只能使用數(shù)據(jù)。改進(jìn)如下:
import React from 'react'; import ReactDOM from 'react-dom'; let _state = [] let index = 0 function myUseState(initialValue){ const currentIndex = index if(_state[currentIndex] === undefined){ _state[currentIndex] = initialValue } const setState = (newValue)=>{ _state[currentIndex] = newValue render() } index++ return [_state[currentIndex], setState] } function render(){ index = 0 ReactDOM.render(<App/>,document.getElementById('root')); } function App(){ const [n, setN] = myUseState(0) const [m, setM] = myUseState(0) return ( <div> n: {n} <button onClick={() => setN(n+1)}>+1</button> <br/> m: {m} <button onClick={() => setM(m+1)}>+1</button> </div> ) } ReactDOM.render(<App/>,document.getElementById('root'));
4. 實(shí)現(xiàn)原理引發(fā)的 Hooks 規(guī)則
上述實(shí)現(xiàn)的 myUseState 肯定不是 React.useState 的具體實(shí)現(xiàn)代碼,但實(shí)現(xiàn)原理是一致的。myUseState 函數(shù)封裝了函數(shù)組件內(nèi)的數(shù)據(jù)狀態(tài),并對該狀態(tài)進(jìn)行管理,以暴露出相關(guān)的操作接口的方式提供給函數(shù)組件使用。
這樣一來,函數(shù)組件就和其數(shù)據(jù)狀態(tài)分離了,函數(shù)組件只負(fù)責(zé)返回虛擬 DOM 本身就可以了,對于數(shù)據(jù)狀態(tài)的管理完全交給其 “鉤” 住的 React.useState Hook 就可以了。
從上述的實(shí)現(xiàn)思路可以發(fā)現(xiàn),React Hooks 的實(shí)現(xiàn)其實(shí)是基于 全局變量 和 閉包 原理實(shí)現(xiàn)的特殊函數(shù)。
但是,正是因?yàn)檫@樣的實(shí)現(xiàn)方式,限制了 React Hooks 的使用必須是 只在頂層調(diào)用Hook,意思就是說 不要在循環(huán),條件或嵌套函數(shù)中調(diào)用 Hook,如果在 if 條件句中使用了 Hook, 導(dǎo)致組件每次渲染生成時(shí) React.useState 語句的執(zhí)行次數(shù)不對,就會(huì)打亂 index 的計(jì)數(shù),從而導(dǎo)致數(shù)據(jù)維護(hù)的錯(cuò)誤。
上述的實(shí)現(xiàn)原理依賴于 index 的正確計(jì)數(shù),因此 React 依賴于調(diào)用 Hooks 的順序,
以上就是詳解React Hooks是如何工作的的詳細(xì)內(nèi)容,更多關(guān)于詳解React Hooks的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React?中使用?RxJS?優(yōu)化數(shù)據(jù)流的處理方案
這篇文章主要為大家介紹了React?中使用?RxJS?優(yōu)化數(shù)據(jù)流的處理方案示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02從頭寫React-like框架的工程搭建實(shí)現(xiàn)
這篇文章主要介紹了從頭寫React-like框架的工程搭建實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04React文件名和目錄規(guī)范最佳實(shí)踐記錄(總結(jié)篇)
React在使用時(shí)非常靈活,如果沒有一個(gè)規(guī)范約束項(xiàng)目,在開發(fā)過程中會(huì)非常混亂,本文將介紹幾個(gè)優(yōu)秀的規(guī)范,介紹文件名和目錄前,需要先簡述一下幾種通用的類型,用來區(qū)分文件的功能,感興趣的朋友一起看看吧2022-05-05