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