React Hook:手動(dòng)實(shí)現(xiàn)useEffect

react hook中县好,為函數(shù)組件提供了useEffect鉤子,能夠在函數(shù)組件內(nèi)部模擬生命周期函數(shù),并可以在內(nèi)部執(zhí)行副作用操作鳖粟,下面來實(shí)現(xiàn)一下其基本功能:

首先總結(jié)一下其用法:

  1. useEffect要支持兩個(gè)參數(shù),第一個(gè)參數(shù)callback為函數(shù)類型拙绊,第二個(gè)參數(shù)可不傳向图,如果傳入,則必須為數(shù)組類型标沪。
  2. 如果第二個(gè)參數(shù)不傳榄攀,則每次狀態(tài)更改函數(shù)重新執(zhí)行都會(huì)執(zhí)行callback
  3. 如果第二個(gè)參數(shù)傳入空數(shù)組,則只有初始化時(shí)執(zhí)行一次
  4. 如果第二個(gè)參數(shù)傳入非空數(shù)組金句,則每當(dāng)依賴項(xiàng)中的狀態(tài)更改檩赢,都會(huì)執(zhí)行callback

實(shí)現(xiàn)代碼如下

// 用來存儲(chǔ)每次調(diào)用useEffect時(shí)傳入的依賴數(shù)組
let prevDepsAry = []
// 用索引記錄每個(gè)回調(diào)函數(shù)對(duì)應(yīng)的依賴數(shù)組
let effectIndex = 0

function useEffect(callback, depsAry) {
  // 先判斷參數(shù)類型是否正確
  // 如果callback不是函數(shù)類型,直接報(bào)錯(cuò)
  if(Object.prototype.toString.call(callback) !== '[object Function]') throw new Error(`${callback} 必須是一個(gè)函數(shù)類型`)
  // 判斷依賴數(shù)組有沒有傳入
  if(depsAry === undefined) {
    // 沒傳入則每次函數(shù)重新調(diào)用都要執(zhí)行回調(diào)函數(shù)
    callback()
  } else {
    // 判斷depsAry是否是一個(gè)數(shù)組類型违寞,如果不是贞瞒,直接報(bào)錯(cuò)
    if(Object.prototype.toString.call(depsAry) !== '[object Array]') throw new Error(`${depsAry} 必須是一個(gè)數(shù)組類型`)
    // 是數(shù)組類型,則需要獲取上一次的依賴數(shù)組趁曼,逐項(xiàng)對(duì)比是否發(fā)生改變
    let prevDeps = prevDepsAry[effectIndex]
    // 判斷是否發(fā)生改變军浆,判斷prevDeps是否存在
    const hasChanged = prevDeps ? depsAry.every((dep, index) => dep === prevDeps[index]) === false : true
    if(hasChanged) {
      // 有依賴發(fā)生改變,調(diào)用callback
      callback()
    }
    // 同步本次更改后的依賴數(shù)組
    prevDepsAry[effectIndex] = depsAry
    effectIndex++
  }
}

// 因?yàn)闋顟B(tài)更改要刷新視圖彰阴,因此這里用ReactDom.render方法來模擬更改狀態(tài)后刷新視圖的操作
function render() {
  // 每次調(diào)用render都要重置stateIndex瘾敢,否則對(duì)應(yīng)的索引無限遞增將無法正確匹配state和setState之間的關(guān)系
  // stateIndex = 0
  // 每次調(diào)用render都要重置effectIndex,否則對(duì)應(yīng)的索引無限遞增會(huì)導(dǎo)致callback匹配不到對(duì)應(yīng)的依賴數(shù)組
  effectIndex = 0
  ReactDom.render(<App />, document.getElementById('root'))
}

上面useEffect的基本功能就已實(shí)現(xiàn)尿这。

下面附上完整的測(cè)試代碼(其中還包含自行封裝的useState函數(shù)簇抵,可將其替換成react自帶的):

import React from 'react'
import ReactDom from 'react-dom'

// 存儲(chǔ)狀態(tài)的數(shù)組
let state = []
// 存儲(chǔ)更改狀態(tài)方法的數(shù)組
let setters = []
// 用來記錄狀態(tài)和更改狀態(tài)方法對(duì)應(yīng)關(guān)系的下標(biāo)
let stateIndex = 0

function createSetter(index) {
  return function (newState) {
    state[index] = newState
    render()
  }
}

function useState(initialState) {
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState
  // 采用閉包緩存每個(gè)state對(duì)應(yīng)的setState
  setters.push(createSetter(stateIndex))
  const value = state[stateIndex]
  const setter = setters[stateIndex]
  // 每創(chuàng)建完一組都要+1,用來作為下一組狀態(tài)的索引
  stateIndex++
  return [value, setter]
}

// 因?yàn)闋顟B(tài)更改要刷新視圖射众,因此這里用ReactDom.render方法來模擬更改狀態(tài)后刷新視圖的操作
function render() {
  // 每次調(diào)用render都要重置stateIndex碟摆,否則對(duì)應(yīng)的索引無限遞增將無法正確匹配state和setState之間的關(guān)系
  stateIndex = 0
  // 每次調(diào)用render都要重置effectIndex,否則對(duì)應(yīng)的索引無限遞增會(huì)導(dǎo)致callback匹配不到對(duì)應(yīng)的依賴數(shù)組
  effectIndex = 0
  ReactDom.render(<App />, document.getElementById('root'))
}

// 用來存儲(chǔ)每次調(diào)用useEffect時(shí)傳入的依賴數(shù)組
let prevDepsAry = []
// 用索引記錄每個(gè)回調(diào)函數(shù)對(duì)應(yīng)的依賴數(shù)組
let effectIndex = 0

function useEffect(callback, depsAry) {
  // 先判斷參數(shù)類型是否正確
  // 如果callback不是函數(shù)類型叨橱,直接報(bào)錯(cuò)
  if(Object.prototype.toString.call(callback) !== '[object Function]') throw new Error(`${callback} 必須是一個(gè)函數(shù)類型`)
  // 判斷依賴數(shù)組有沒有傳入
  if(depsAry === undefined) {
    // 沒傳入則每次函數(shù)重新調(diào)用都要執(zhí)行回調(diào)函數(shù)
    callback()
  } else {
    // 判斷depsAry是否是一個(gè)數(shù)組類型典蜕,如果不是,直接報(bào)錯(cuò)
    if(Object.prototype.toString.call(depsAry) !== '[object Array]') throw new Error(`${depsAry} 必須是一個(gè)數(shù)組類型`)
    // 是數(shù)組類型罗洗,則需要獲取上一次的依賴數(shù)組愉舔,逐項(xiàng)對(duì)比是否發(fā)生改變
    let prevDeps = prevDepsAry[effectIndex]
    // 判斷是否發(fā)生改變,判斷prevDeps是否存在
    const hasChanged = prevDeps ? depsAry.every((dep, index) => dep === prevDeps[index]) === false : true
    if(hasChanged) {
      // 有依賴發(fā)生改變伙菜,調(diào)用callback
      callback()
    }
    // 同步本次更改后的依賴數(shù)組
    prevDepsAry[effectIndex] = depsAry
    effectIndex++
  }
}

// 手動(dòng)實(shí)現(xiàn)useState useEffect
function App() {

  const [count, setCount] = useState(0)
  const [title, setTitle] = useState('useState')

  useEffect(() => {
    console.log('useEffect');
  }, [])

  useEffect(() => {
    console.log('useEffect count');
  }, [count])

  useEffect(() => {
    console.log('useEffect every time');
  })

  return (
    <div>
      <h1>{title}</h1>
      <button onClick={() => setTitle('useState Success')}>修改標(biāo)題</button>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>count++</button>
    </div>
  )
}

export default App
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末轩缤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌火的,老刑警劉巖壶愤,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異馏鹤,居然都是意外死亡征椒,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門湃累,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勃救,“玉大人,你說我怎么就攤上這事脱茉〖艚妫” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵琴许,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我溉躲,道長(zhǎng)榜田,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任锻梳,我火速辦了婚禮箭券,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疑枯。我一直安慰自己辩块,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布荆永。 她就那樣靜靜地躺著废亭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪具钥。 梳的紋絲不亂的頭發(fā)上豆村,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音骂删,去河邊找鬼掌动。 笑死,一個(gè)胖子當(dāng)著我的面吹牛宁玫,可吹牛的內(nèi)容都是我干的粗恢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼欧瘪,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼眷射!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤凭迹,失蹤者是張志新(化名)和其女友劉穎罚屋,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗅绸,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脾猛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鱼鸠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猛拴。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蚀狰,靈堂內(nèi)的尸體忽然破棺而出愉昆,到底是詐尸還是另有隱情,我是刑警寧澤麻蹋,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布跛溉,位于F島的核電站,受9級(jí)特大地震影響扮授,放射性物質(zhì)發(fā)生泄漏芳室。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一刹勃、第九天 我趴在偏房一處隱蔽的房頂上張望堪侯。 院中可真熱鬧,春花似錦荔仁、人聲如沸伍宦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽次洼。三九已至,卻和暖如春掌呜,著一層夾襖步出監(jiān)牢的瞬間滓玖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工质蕉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留势篡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓模暗,卻偏偏與公主長(zhǎng)得像禁悠,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子兑宇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1碍侦、值捕獲 造成數(shù)據(jù)不一致 異常 閉包內(nèi)部變量為值捕獲。如例子,點(diǎn)擊按鈕瓷产,設(shè)置age為1站玄,調(diào)用request方法,...
    請(qǐng)叫我啊亮閱讀 10,794評(píng)論 0 0
  • 一.認(rèn)識(shí)Effect Hook 在Hook中濒旦,如何對(duì)應(yīng)生命周期呢株旷?: Effect Hook起到了類似class中...
    _生生不息_閱讀 464評(píng)論 0 1
  • 要點(diǎn): 可以在不編寫class的情況下使用 state 以及其他react特性。 Hook沒有破壞性改動(dòng)尔邓, 完全是...
    koala949閱讀 861評(píng)論 0 0
  • react 16.8 以后加上了 react hook晾剖,它可以讓你在不編寫 class 的情況下使用 state ...
    forever_提拉米蘇閱讀 524評(píng)論 1 6
  • 一、React介紹 溫馨提醒:想要獲取更好的觀看效果梯嗽,可以點(diǎn)擊查看本篇文章的原文檔(React-Hook快速入門(...
    SwiftBirds閱讀 457評(píng)論 0 1