我嘗試重現(xiàn) React 的 useState() Hook 并失去了工作機(jī)會(huì)
在 React 中創(chuàng)建自定義鉤子(hooks)是一個(gè)非常有價(jià)值的練習(xí),甚至可以說是許多開發(fā)者的“啊哈”時(shí)刻。
劇透警告:第一次嘗試是在 Revolut 的一次高壓面試中,結(jié)果并不如意。
在這篇文章中,帶你一起回顧我重現(xiàn)著名的 useState() 鉤子的歷程,稱它為 useMyState()。同時(shí),我也會(huì)給出一些建議,幫助你下次面試時(shí)避免失敗。
讓我們一起來探討哪里出了問題,我是如何重新拾起信心的,以及你如何避免犯同樣的錯(cuò)誤。準(zhǔn)備好迎接一些高級(jí) React 的樂趣吧!??
夢(mèng)寐以求的工作和艱難的面試
拿到 Revolut 的面試機(jī)會(huì)感覺就像中了彩票。我滿懷激動(dòng)和信心,因?yàn)槲一藬?shù)周時(shí)間打磨我的 React 和 TypeScript 技能。
我以為我已經(jīng)掌握了一切——但面試讓我意識(shí)到,我的信心過于自滿。這個(gè)面試,我既興奮又緊張,然而很快,這次經(jīng)歷變成了一次讓我永生難忘的挫敗。
在順利通過一些初步面試輪次后,我遇到了一個(gè)我沒有完全預(yù)料到的挑戰(zhàn):“你能從頭開始重現(xiàn) useState() 鉤子嗎?”
這個(gè)問題看似簡(jiǎn)單,但其背后的意義深遠(yuǎn)。我知道 useState() 在高層次上的工作原理,但在面試現(xiàn)場(chǎng),面對(duì)時(shí)鐘滴答作響,要從頭構(gòu)建它卻是另一回事。
任務(wù):在壓力下重現(xiàn) useState()
面試官解釋說,我需要?jiǎng)?chuàng)建一個(gè)自定義鉤子,模仿 React 的 useState() 的行為。
這個(gè)鉤子應(yīng)該存儲(chǔ)狀態(tài),允許狀態(tài)更新,并在狀態(tài)變化時(shí)觸發(fā)重新渲染。
聽起來很熟悉,對(duì)吧?我曾經(jīng)讀過這類內(nèi)容,甚至考慮過如何實(shí)現(xiàn)。但現(xiàn)在,在面試壓力下,一切似乎變得更加復(fù)雜。
關(guān)鍵問題是:我從未實(shí)際在現(xiàn)場(chǎng)實(shí)現(xiàn)過類似的東西。
概念在我腦海中很清晰,但在面對(duì)一個(gè)可能決定我職業(yè)生涯的人時(shí),執(zhí)行起來卻完全不同。
最終,我沒有完成這個(gè)任務(wù)……但后來我決定在沒有時(shí)間壓力的情況下再試一次,結(jié)果是這樣的。
為什么要重現(xiàn) useState()?
你可能會(huì)問,“為什么要重現(xiàn)一個(gè)已經(jīng)完美運(yùn)作的東西?”嗯,這正是我為什么要做的原因——因?yàn)樗_實(shí)運(yùn)作得很好。
理解 useState() 的底層工作原理有助于你更好地掌握 React 的“魔法”。
此外,構(gòu)建你自己的工具——即使它們已經(jīng)存在——也是一種非常有成就感的體驗(yàn)。
入門:useMyState() 背后的概念
在開始編碼之前,讓我們明確我們想要實(shí)現(xiàn)的目標(biāo)。React 中的 useState() 鉤子:
- 返回一個(gè)狀態(tài)變量和一個(gè)用于更新該狀態(tài)的函數(shù)。
- 每當(dāng)狀態(tài)更新時(shí),重新渲染組件。
我們的 useMyState() 鉤子應(yīng)該實(shí)現(xiàn)相同的功能。本質(zhì)上,我們需要一種方式來存儲(chǔ)狀態(tài),更新它,并在發(fā)生變化時(shí)觸發(fā)重新渲染。聽起來很簡(jiǎn)單,對(duì)吧?嗯,這既簡(jiǎn)單又復(fù)雜。
構(gòu)建模塊:useState() 的底層工作原理
首先,讓我們討論一下 React 如何在內(nèi)部管理狀態(tài)。當(dāng)你調(diào)用 useState() 時(shí),React 在內(nèi)部通過一個(gè)稱為“鉤子狀態(tài)隊(duì)列”的機(jī)制來跟蹤你的狀態(tài)。
每當(dāng)你調(diào)用更新函數(shù)(我們稱之為 setState)時(shí),React 并不會(huì)立即更新狀態(tài)。相反,它會(huì)調(diào)度一次重新渲染。
在這次重新渲染期間,React 根據(jù)隊(duì)列計(jì)算新的狀態(tài)。
要復(fù)制這種行為,我們需要:
- 將狀態(tài)存儲(chǔ)在一個(gè)可以跨渲染持續(xù)存在的地方。
- 提供一個(gè)函數(shù)來更新這個(gè)狀態(tài)并觸發(fā)重新渲染。
創(chuàng)建 useMyState():編寫一些代碼!
我們可以這樣開始。首先,我們需要?jiǎng)?chuàng)建一個(gè)組件級(jí)別的狀態(tài)存儲(chǔ)。
由于 React 并不提供一種直接的方式來在渲染之間保持變量的持久性(除了使用類似 useState() 或 useRef() 的鉤子),我們需要使用 useRef() 來實(shí)現(xiàn)這一點(diǎn)。
function useMyState(initialValue) {
const stateRef = React.useRef(initialValue);
const [, forceRender] = React.useReducer(x => x + 1, 0);
const setState = (newValue) => {
stateRef.current = newValue;
forceRender();
};
return [stateRef.current, setState];
}
細(xì)分解釋
- useRef(initialValue): 這是我們的狀態(tài)所在。useRef() 可以在渲染之間保持值,而不會(huì)導(dǎo)致重新渲染。
- useReducer(x => x + 1, 0): 這是一個(gè)巧妙的技巧,用來強(qiáng)制組件重新渲染。我們不關(guān)心 x 的值,只是調(diào)用 forceRender() 會(huì)使 React 重新渲染我們的組件。
- setState(newValue): 我們更新 stateRef 的 current 屬性,然后觸發(fā)一次重新渲染。
就這樣!通過這三部分,我們就有了自己的 useState() 版本。但我們?cè)趺粗浪欠裼行??讓我們測(cè)試一下。
測(cè)試 useMyState(): 讓它工作
讓我們看看這個(gè)鉤子的實(shí)際效果。這里有一個(gè)簡(jiǎn)單的計(jì)數(shù)器組件,使用我們的 useMyState():
function Counter() {
const [count, setCount] = useMyState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
當(dāng)你點(diǎn)擊“Increment”按鈕時(shí),計(jì)數(shù)器應(yīng)該增加,組件會(huì)重新渲染以反映新的計(jì)數(shù)。這就像使用 useState() 一樣。很棒吧?
處理多個(gè)狀態(tài):擴(kuò)展 useMyState()
現(xiàn)在,如果我們需要在同一個(gè)組件中管理多個(gè)狀態(tài)會(huì)怎么樣?React 的 useState() 可以優(yōu)雅地處理這個(gè)問題,我們的 useMyState() 也可以。
function MultipleStatesComponent() {
const [count, setCount] = useMyState(0);
const [text, setText] = useMyState('Hello');
return (
<div>
<p>{count}</p>
<p>{text}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setText('World')}>Change Text</button>
</div>
);
}
這個(gè)組件管理兩個(gè)獨(dú)立的狀態(tài)變量,useMyState() 像 useState() 一樣處理它們。
總結(jié)
React 的 useState() 鉤子經(jīng)過高度優(yōu)化、經(jīng)過實(shí)戰(zhàn)檢驗(yàn),能夠處理我們簡(jiǎn)單的 useMyState() 可能忽略的邊緣情況。
盡管我未能在面試中重現(xiàn)它,但我能夠在一個(gè)平靜的環(huán)境中自己重新實(shí)現(xiàn)它,下次面試時(shí),我會(huì)準(zhǔn)備得更好。
重現(xiàn)這樣的工具并不是為了取代它們,而是為了學(xué)習(xí)它們的工作原理,從而更好地使用它們。
誰知道呢?你也許會(huì)在一個(gè)副項(xiàng)目中找到 useMyState() 的用武之地,或者只是把它當(dāng)作一個(gè)有趣的技巧留在你的工具箱里。
祝你好運(yùn)!