【譯】React Hooks的5個重要原則
- 1987字
- 10分鐘
- 2024-07-27
在當下,React 無疑仍然是創建前端應用程序的最受歡迎選擇之一。這並不是因為它沒有缺點,而是因為 React 多年來積累了龐大的社區和巨大的人氣。
很難想象沒有 Hook 的 React,但不幸的是,我們經常看到開發人員過度使用它們。結果,他們不得不解決依賴數組、無用的重新渲染以及它們造成的所有混亂問題,因為他們內心深處擔心,在 React 中,只有使用 Hook 才能完成所有事情。
在本文中,我將討論每個新 React 程式設計師應該知道的五個原則,以改進和簡化他們的代碼。
1. 不是每個函數都必須是一個 Hook
讓我們從基礎開始,先看看定義。
Hook 使用 JavaScript 函數定義,但它們代表一種特殊的可重用 UI 邏輯,並且它們在調用位置上有限制。當你使用 Hook 時,需要遵循 Hook 的規則。
Hooks 看起來像函數,但有一些區別:
- Hooks 只能在函數組件或自訂 Hook 中使用。
- Hook 的名稱總是以“use”開頭,後跟一個大寫字母。
- 如果自訂 Hook 內部不包含對其他 Hook 的調用,它就是一個函數,而不是一個 Hook。這很重要,因為它有助於確定是否有一些狀態邏輯或副作用在其中。
讓我們創建一個名為 useBoolean 的簡單自訂 Hook,以滿足這些要求。我們可以用它來打開/關閉面板、對話框、顯示/隱藏元素等。
如果查看官方文檔,您會發現建議將 Hook 返回的任何函數包裝在 useCallback 中,我們也將這樣做。
1interface ICallbacks {2 setFalse: () => void;3 setTrue: () => void;4 toggle: () => void;5}6
7const useBoolean = (initialValue: boolean): [boolean, ICallbacks] => {8 const [value, setValue] = useState(initialValue);9
10 const setFalse = useCallback(() => {11 setValue(false);12 }, []);13
14 const setTrue = useCallback(() => {15 setValue(true);16 }, []);17
18 const toggle = useCallback(() => {19 setValue((curValue) => !curValue);20 }, []);21
22 return [value, { setFalse, setTrue, toggle }];23};
記住基礎知識後,我們可以更深入地了解一些細微差別。
2. 了解重新渲染
你可能會問為什麼。
理解 React 如何工作以及當您通過 setter 函數更改組件的狀態時會發生什麼,這一點很重要。乍一看,您似乎改變了狀態,結果必須立即出現,但事實是這樣嗎?
當你知道改變狀態發生了什麼時,就更容易理解為什麼以及何時 useEffect 或其他帶依賴數組的 Hook 會被觸發。
讓我們看看一個簡單的例子。想象一下,我們第一次按下按鈕並調用 onChangeText,傳遞值“newValue”。
1const [text, setText] = useState("defaultValue");2
3const onChangeText = (value: string) => {4 // value 等於 "newValue"5 setText(value);6
7 console.log(text); // 這裡的值會是什麼?8};
看起來我們應該在控制台中看到“newValue”,但實際上會是“defaultValue”。為什麼?因為新值只有在重新渲染後才可用。
有必要看到發生的步驟:
- 我們通過 setter 函數改變狀態,告訴 React 採取行動。
- 渲染。React 調用你的組件以計算新的 JSX,這將被返回。
- 提交。計算完變化後,React 將修改 DOM;最小的動作將被應用。
- 在前面的步驟之後,您將在屏幕上看到視覺變化(“瀏覽器渲染”)。
每次你想改變狀態中的值時,都需要記住這些步驟每次都會完成。
3. useState 並不總是正確的答案
在 React 中,我們有兩種管理組件狀態的方法——useState 和 useReducer。第二種方法不太流行,因為它適用於狀態中更複雜的對象,老實說,對於新程式設計師來說,乍一看它看起來太複雜了,但事實並非如此。
然而,useState 看起來非常簡單易懂,所以新程式設計師往往使用它的次數超過了實際需要。
它旨在根據使用者互動來管理重新繪製組件的狀態。如果你想記住一些內容而不進行渲染,可能不應該將其放在狀態中。useRef 會是更好的選擇。
如果:
- 你想在重新渲染期間記住一些值而不向使用者展示它們。
- 你已經在狀態中有數據,或者你通過 props 接收它,但需要轉換它;你不需要將新值保存在新的 useState 對象中,創建一個新變量並操作它而不會觸發無用的重新渲染。
你需要將值保存在狀態中,如果:
- 你想在值改變時重新繪製組件;最常見的例子是顯示/隱藏面板、旋轉器、錯誤消息以及修改數組。
將你的代碼簡化如下:
1/**2以下代碼導致無用的重新渲染和不必要的 useEffect 使用。3當 name 或 description 變化是,React 會重新渲染組件4**/5
6const [name, setName] = useState("name");7const { description, index } = props;8const [fullName, setFullName] = useState("");9
10useEffect(() => {11 setFullName(`${name} - ${description}`);12}, [name, description]);
更改為:
1/**2我們可以使用 React 的默認行為,在 name3或 description 更改時獲取正確的值,4而不觸發一次更多的重新渲染。5**/6const [name, setName] = useState();7const { description, index } = props;8const nameWithDescription = `${name} - ${description}`;
4. 使用 useEffect 時需要非常小心
useEffect 是一個 React Hook,它讓你可以同步一個組件和外部系統。
但實際上,我們使用 useEffect 的次數遠超實際需要。它非常適合在組件掛載時獲取數據,但不幸的是,新程式設計師傾向於使用這個 Hook 來改變狀態,這不是最好的解決方案。
如果發現自己在一個組件內部寫一個又一個 useEffect,請停下來審查代碼。通常你不需要它們,而且你可以很快消除它們。
如果符合以下情況則不需要 useEffect:
- 你需要處理使用者事件(click);如果你知道哪些動作會觸發某些邏輯,不要使用 useEffect 來調用該邏輯。
- 你需要轉換數據以進行渲染,例如從狀態或 props 中連接字符串。將響應性值或邏輯放入依賴數組中可能會導致 useEffect 被過於頻繁地調用,導致無限迴圈。
如果出現以下情況,您需要選擇 useEffect:
- 你想在組件掛載時獲取數據,設置間隔,並用它來同步狀態與其他系統
5. 不要害怕 useRef
不幸的是,useRef 被低估了。它不是最流行的 Hook 之一,但它非常有用。知道如何以及在哪裡使用它可以取得很好的效果。
讓我們從基礎開始。
useRef 是一個 React Hook,它讓你可以引用不需要渲染的值。——來自 React 官方文檔。
無論你是在創建一個引用 DOM 中的節點的 JavaScript 對象還是一個簡單的值,React 會記住你通過 useRef 創建的值,並且它不會在重新渲染期間丟失。
它給我們帶來了什麼?
- 我們可以輕鬆訪問 DOM 中的元素。例如,你可以獲取輸入字段的值,聚焦到特定元素,獲取它們的高度和寬度,滾動到屏幕的特定部分等。
- 你可以記住任何你需要的數據而不重新渲染組件。例如,如果你需要一個計數器或計時器,選擇 useRef 而不是 useState。
例子:
1// 引用一個數字2const refCount= useRef(0);3// 引用一個輸入元素4const refInputField = useRef(null);5
6/**7要訪問該值,你需要使用 current 屬性。8useRef 不會觸發重新渲染,所以你可以在 useEffect 內部使用它9而不用擔心依賴數組10*/11
12const onClick = () => {13 refCount.current = refCount.current + 1;14 refInputField.current.focus();15}16
17return (18 <>19 <button onClick={onClick}>20 Click me!21 </button>22 <input ref={refInputField}></input>23 </>24);