關於React狀態管理的三個規則總結
目錄
- 前言
- No.1 一個關注點
- No.2 提取複雜的狀態邏輯
- No.3 提取多個狀態操作
- 總結
前言
React 元件內部的狀態是在渲染過程之間保持不變的封裝資料。useState() 是 React hook,負責管理功能元件內部的狀態。
我喜歡 useState() ,它確實使狀態處理變得非常容易。但是我經常遇到類似的問題:
- 我應該將元件的狀態劃分為小狀態,還是保持複合狀態?
- 如果狀態管理變得複雜,我應該從元件中提取它嗎?該怎麼做?
- 如果 useState() 的用法是如此簡單,那麼什麼時候需要 useReducer()?
本文介紹了 3 條簡單的規則,可以回答上述問題,並幫助你設計元件的狀態。
No.1 一個關注點
有效狀態管理的第一個規則是:
使狀態變數負責一個問題。
使狀態變數負責一個問題使其符合單一責任原則。
讓我們來看一個複合狀態的示例,即一種包含多個狀態值的狀態。
const [state,setState] = useState({ on: true,count: 0 }); state.on // => true state.www.cppcns.comcount // => 0
狀態由一個普通的 物件組成,該物件具有 on 和 count 屬性。
第一個屬性 state.on 包含一個布林值,表示開關。同樣,``state.count` 包含一個表示計數器的數字,例如,使用者單擊按鈕的次數。
然後,假設你要將計數器加1:
// Updating compound state setUser({ ...state,count: state.count + 1 });
你必須將整個狀態放在一起,才能僅更新 count。這是為了簡單地增加一個計數器而呼叫的一個大結構:這都是因為狀態變數負責兩個方面:開關和計數器。
解決方案是將複合狀態分為兩個原子狀態 on 和 count:
const [on,setOnOff] = useState(true); const [count,setCount] = useState(0);
狀態變數 on 僅負責儲存開關狀態。同樣,count 變數僅負責計數器。
現在,讓我們嘗試更新計數器:
setCount(count + 1); // or using a callback setCount(count => count + 1);
count 狀態僅負責計數,很容易推斷,也很容易更新和讀取。
不必擔心呼叫多個 useState() 為每個關注點建立狀態變數。
但是請注意,如果你使用過多的 useState() 變數,則你的元件很有可能就違反了“單一職責原則”。只需將此類元件拆分為較小的元件即可。
No.2 提取複雜的狀態邏輯
將複雜的狀態邏輯提取到自定義 hook 中。
在元件內保留複雜的狀態操作是否有意義?
答案來自基本面(通常會發生這種情況)。
建立 React hook 是為了將元件與複雜狀態管理和副作用隔離開。因此,由於元件只應關注要渲染的元素和要附加的某些事件偵聽器,所以應該把複雜的狀態邏輯提取到自定義 hook 中。
考慮一個管理產品列表的元件。使用者可以新增新的產品名稱。約束是產品名稱必須是唯一的。
第一次嘗試是將產品名稱列表的設定程式直接保留在元件內部:
function ProductsList() { const [names,setNames] = useState([]); const [newName,setNewName] = useState(''); const map = name => <div>{name}</div>; const handleChange = event => setNewName(event.target.value); const handleAdd = () => { const s = new Set([...names,newName]); setNames([...s]); }; return ( <div className="products"> {names.map(map)} <input type="text" onChange={handleChange} /> <button onClick={handleAdd}>Add</button> </div> ); }
names 狀態變數儲存產品名稱。單擊 Add 按鈕時,將呼叫 addNewProduct() 事件處理程式。
在 addNewProduct() 內部,用 Set 物件來保持產品名稱唯一。元件是否應該關注這個實現細節?不需要。
最好將複雜的狀態設定器邏輯隔離到一個自定義 hook 中。開始做吧。
新的自定義鉤子 useUnique() 可使每個專案保持唯一性:
// useUnique. export function useUnique(initial) { const [items,setItems] = useState(initial); const add = newItem => { const uniqueItems = [...new Set([...items,newIteknoEAXAm])]; setItems(uniqueItems); }; return [items,add]; };
將自定義狀態管理提取到一個 hook 中後,ProductsList 元件將變得更加輕巧:
import { useUnique } from www.cppcns.com'./useUnique';
function ProductsList() {
const [names,add] = useUnique([]); const [newName,setNewName] = useState('');
const map = name => <div>{name}</div>;
const handleChange = event => setNewName(e.target.value);
const handleAdd = () => add(newName);
return (
<div className="products">
{names.map(map)}
<input type="text" onChange={handleChange} />
<button onClick={handleAdd}>Add</button>
</div>
);
}
const [names,addName] = useUnique([]) 啟用自定義 hook。該元件不再被複雜的狀態管理所困擾。
如果你想在列表中新增新名稱,則只需呼叫 add('New Product Name') 即可。
最重要的是,將複雜的狀態管理提取到自定義 hooks 中的好處是:
- 該元件不再包含狀態管理的詳細資訊
- 自定義 hook 可以重複使用
- 自定義 hook 可輕鬆進行隔離測試
No.3 提取多個狀態操作
將多個狀態操作提取到化簡器中。
繼續用 ProductsList 的例子,讓我們引入“delete”操作,該操作將從列表中刪除產品名稱。
現在,你必須為 2 個操作編碼:新增和刪除產品。處理這些操作,就可以建立一個簡化器並使元件擺脫狀態管理邏輯。
同樣,此方法符合 hook 的思路:從元件中提取複雜的狀態管理。
以下是新增和刪除產品的 reducer 的一種實現:
function uniqueReducer(state,action) { switch (action.type) { case 'add': return [...new Set([...state,action.name])]; case 'delete': return state.filter(name => name === action.name); default: throw new Error(); } }
然後,可以通過呼叫 React 的 useReducer() hook 在產品列表中使用 uniqueReducer():
function ProductsList() { const [names,dispatch] = useReducer(uniqueReducer,[]); const [newName,setNewName] = useState(''); const handleChange = event => setNewName(event.target.value); const handleAdd = () => dispatch({ type: 'add',name: newName }); const map = name => { const delete = () => dispatch({ type: 'delete',name }); return ( <div> {name} <button onClick={delete}>Delete</button> </div> ); } return ( <div className="products"> {names.map(map)} <input type="text" onChange={handleChange} /> <button onClick={handleAdd}>Add</button> </div> ); }
const [names,[]) 啟用 uniqueReducer。names 是儲存產品名稱的狀態變數,而 dispatch 是使用操作物件呼叫的函式。
當單擊 Add 按鈕時,處理程式將呼叫 dispatch({ type: 'add',name: newName })。排程一個 add 動作使 reducer uniqueReducer 向狀態新增一個新的產品名稱。
以同樣的方式,當單擊 Delete 按鈕時,處理程式將呼叫 dispatch({ type: 'delete',name })。remove 操作將產品名稱從名稱狀態中刪除。
有趣的是,reducer 是命令模式的特例。
總結
狀態變數應只關注一個點。
如果狀態具有複雜的更新邏輯,則將該邏輯從元件提取到自定義 hook 中。
同樣,如果狀態需要多個操作,請用 reducer 合併這些操作。
無論你使用什麼規則,狀態都應該儘可能地簡單和分離。元件不應被狀態更新的細節所困擾:它們應該是自定義 hook 或化簡器的一部分。
這 3 個簡單的規則能夠使你的狀態邏輯易於理解、維護和測試。
到此這篇關於React狀態管理的三個規則的文章就介紹到這了,更多相關React狀態管理內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!