詳解React Hooks是如何工作的
1. React Hooks VS 純函數(shù)
React Hook 說白了就是 React V18.6 新增的一些 API,API的本質(zhì)就是提供某種功能的函數(shù)接口。因此,React Hooks 就是一些函數(shù),但是 React Hooks 不是純函數(shù)。
什么是純函數(shù)呢?就是此函數(shù)在相同的輸入值時,需產(chǎn)生相同的輸出,并且此函數(shù)不能影響到外面的數(shù)據(jù)。
簡單理解就是函數(shù)里面不能用到在外面定義的變量,因?yàn)槿绻玫搅送饷娑x的變量,當(dāng)外面的變量改變時會影響函數(shù)內(nèi)部的計(jì)算,函數(shù)也會影響到外面的變量。
對于 React Hooks 提供的函數(shù) API,恰恰就不是純函數(shù)。
來看一個 useState 的使用語句 const [count, setCount] = useState(0),使用 useState 函數(shù)得到的結(jié)果并不是全都一樣的,因?yàn)槿绻?useState(0) 每次得到的結(jié)果都是一樣的,那 count 值就永遠(yuǎn)不會改變了,那 count 所在的頁面就永遠(yuǎn)不會改變,和我們看到的結(jié)果就不一樣了。由此可知,React Hooks 都不是純函數(shù),也就是說 Hooks 用到了函數(shù)外的變量。
那么是什么特性讓 React Hooks 一定不能是純函數(shù)呢?實(shí)際上是 React 框架和函數(shù)組件本身決定的。我們知道,React 頁面渲染的原理就是通過每次 render 得到新的虛擬 DOM ,然后進(jìn)行 DOM Diff 來渲染頁面。而 React 的函數(shù)組件是通過執(zhí)行整個函數(shù)得到一個虛擬 DOM。因此在每次頁面渲染 render 時,在函數(shù)組件內(nèi)部的所有語句都會重新執(zhí)行一次。如果在函數(shù)組件內(nèi)部使用的 React Hooks 是純函數(shù)的話,就不會在每次渲染后得到不同的虛擬 DOM 了。
React 規(guī)定: 所有 React 組件都必須是純函數(shù),并禁止修改其自身 props 。
因此在 React V16.8 之前 React Hooks 還沒出來的時候,函數(shù)組件因?yàn)槭羌兒瘮?shù),只能返回一個固定的虛擬 DOM,不能包含狀態(tài),也不支持生命周期方法。因此,當(dāng)時僅僅是支持函數(shù)組件,但函數(shù)組件相比于類組件限制太多,函數(shù)組件無法取代類組件,也沒類組件好用。
React 希望組件是簡單的而不是復(fù)雜的,React 認(rèn)為組件的最佳寫法應(yīng)該是函數(shù),而不是類。因此 React 就新增了 React Hooks,Hook 就是鉤子的意思,是 React 提供給函數(shù)組件在需要外部功能和數(shù)據(jù)狀態(tài)時將其 “鉤” 進(jìn)去,從而完善函數(shù)組件,使其能完全代替類組件。
React 的函數(shù)組件只能是純函數(shù),那么每次事件發(fā)生時重新 render 函數(shù)組件時得到不同的虛擬 DOM 的事就完全交給了 React Hooks,那么 React Hooks 是如何做到的呢?下面就手動實(shí)現(xiàn)一個 useState,useState 的具體細(xì)節(jié)肯定不是這樣的,但原理和思路是一樣的。
2. 簡單 myUseState
React.useState 的第一次執(zhí)行是將初始值賦予給一個 _state,之后的每次重新 render 時就是讀取 _state 的值。[state, setState] 中的 setState 做的事就是改變 _state 的值,然后重新渲染頁面。
根據(jù)這個原理實(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 時就會出現(xiàn)問題了,二者共用一個 _state 會出現(xiàn)混亂。
因此需要將上述實(shí)現(xiàn)進(jìn)行改進(jìn),改進(jìn)的思路就是將 _state 定義為一個數(shù)據(jù)或者是對象,由于我們在函數(shù)使用時只傳了一個數(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)致組件每次渲染生成時 React.useState 語句的執(zhí)行次數(shù)不對,就會打亂 index 的計(jì)數(shù),從而導(dǎo)致數(shù)據(jù)維護(hù)的錯誤。
上述的實(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)文章
比ant更豐富Modal組件功能實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了比ant更豐富Modal組件功能實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
解決React報(bào)錯Expected an assignment or funct
這篇文章主要為大家介紹了React報(bào)錯Expected an assignment or function call and instead saw an expression解決方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
解決React報(bào)錯You provided a `checked` prop&n
這篇文章主要為大家介紹了React報(bào)錯You provided a `checked` prop to a form field的解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
React如何將組件渲染到指定DOM節(jié)點(diǎn)詳解
這篇文章主要給大家介紹了關(guān)于React如何將組件渲染到指定DOM節(jié)點(diǎn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)下吧。2017-09-09
React Hooks 實(shí)現(xiàn)和由來以及解決的問題詳解
這篇文章主要介紹了React Hooks 實(shí)現(xiàn)和由來以及解決的問題詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
react-navigation之動態(tài)修改title的內(nèi)容
這篇文章主要介紹了react-navigation之動態(tài)修改title的內(nèi)容,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-09-09

