- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) > web開(kāi)發(fā) > JavaScript >
- 關(guān)于React狀態(tài)管理的三個(gè)規則總結
React 組件內部的狀態(tài)是在渲染過(guò)程之間保持不變的封裝數據。useState() 是 React hook,負責管理功能組件內部的狀態(tài)。
我喜歡 useState() ,它確實(shí)使狀態(tài)處理變得非常容易。但是我經(jīng)常遇到類(lèi)似的問(wèn)題:
本文介紹了 3 條簡(jiǎn)單的規則,可以回答上述問(wèn)題,并幫助你設計組件的狀態(tài)。
有效狀態(tài)管理的第一個(gè)規則是:
使狀態(tài)變量負責一個(gè)問(wèn)題。
使狀態(tài)變量負責一個(gè)問(wèn)題使其符合單一責任原則。
讓我們來(lái)看一個(gè)復合狀態(tài)的示例,即一種包含多個(gè)狀態(tài)值的狀態(tài)。
const [state, setState] = useState({ on: true, count: 0 }); state.on // => true state.count // => 0
狀態(tài)由一個(gè)普通的 JavaScript 對象組成,該對象具有 on 和 count 屬性。
第一個(gè)屬性 state.on 包含一個(gè)布爾值,表示開(kāi)關(guān)。同樣,``state.count` 包含一個(gè)表示計數器的數字,例如,用戶(hù)單擊按鈕的次數。
然后,假設你要將計數器加1:
// Updating compound state setUser({ ...state, count: state.count + 1 });
你必須將整個(gè)狀態(tài)放在一起,才能僅更新 count。這是為了簡(jiǎn)單地增加一個(gè)計數器而調用的一個(gè)大結構:這都是因為狀態(tài)變量負責兩個(gè)方面:開(kāi)關(guān)和計數器。
解決方案是將復合狀態(tài)分為兩個(gè)原子狀態(tài) on 和 count:
const [on, setOnOff] = useState(true); const [count, setCount] = useState(0);
狀態(tài)變量 on 僅負責存儲開(kāi)關(guān)狀態(tài)。同樣,count 變量?jì)H負責計數器。
現在,讓我們嘗試更新計數器:
setCount(count + 1); // or using a callback setCount(count => count + 1);
count 狀態(tài)僅負責計數,很容易推斷,也很容易更新和讀取。
不必擔心調用多個(gè) useState() 為每個(gè)關(guān)注點(diǎn)創(chuàng )建狀態(tài)變量。
但是請注意,如果你使用過(guò)多的 useState() 變量,則你的組件很有可能就違反了“單一職責原則”。只需將此類(lèi)組件拆分為較小的組件即可。
將復雜的狀態(tài)邏輯提取到自定義 hook 中。
在組件內保留復雜的狀態(tài)操作是否有意義?
答案來(lái)自基本面(通常會(huì )發(fā)生這種情況)。
創(chuàng )建 React hook 是為了將組件與復雜狀態(tài)管理和副作用隔離開(kāi)。因此,由于組件只應關(guān)注要渲染的元素和要附加的某些事件偵聽(tīng)器,所以應該把復雜的狀態(tài)邏輯提取到自定義 hook 中。
考慮一個(gè)管理產(chǎn)品列表的組件。用戶(hù)可以添加新的產(chǎn)品名稱(chēng)。約束是產(chǎn)品名稱(chēng)必須是唯一的。
第一次嘗試是將產(chǎn)品名稱(chēng)列表的設置程序直接保留在組件內部:
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 狀態(tài)變量保存產(chǎn)品名稱(chēng)。單擊 Add 按鈕時(shí),將調用 addNewProduct() 事件處理程序。
在 addNewProduct() 內部,用 Set 對象來(lái)保持產(chǎn)品名稱(chēng)唯一。組件是否應該關(guān)注這個(gè)實(shí)現細節?不需要。
最好將復雜的狀態(tài)設置器邏輯隔離到一個(gè)自定義 hook 中。開(kāi)始做吧。
新的自定義鉤子 useUnique() 可使每個(gè)項目保持唯一性:
// useUnique.js export function useUnique(initial) { const [items, setItems] = useState(initial); const add = newItem => { const uniqueItems = [...new Set([...items, newItem])]; setItems(uniqueItems); }; return [items, add]; };
將自定義狀態(tài)管理提取到一個(gè) hook 中后,ProductsList 組件將變得更加輕巧:
import { useUnique } from './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。該組件不再被復雜的狀態(tài)管理所困擾。
如果你想在列表中添加新名稱(chēng),則只需調用 add('New Product Name') 即可。
最重要的是,將復雜的狀態(tài)管理提取到自定義 hooks 中的好處是:
將多個(gè)狀態(tài)操作提取到化簡(jiǎn)器中。
繼續用 ProductsList 的例子,讓我們引入“delete”操作,該操作將從列表中刪除產(chǎn)品名稱(chēng)。
現在,你必須為 2 個(gè)操作編碼:添加和刪除產(chǎn)品。處理這些操作,就可以創(chuàng )建一個(gè)簡(jiǎn)化器并使組件擺脫狀態(tài)管理邏輯。
同樣,此方法符合 hook 的思路:從組件中提取復雜的狀態(tài)管理。
以下是添加和刪除產(chǎn)品的 reducer 的一種實(shí)現:
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(); } }
然后,可以通過(guò)調用 React 的 useReducer() hook 在產(chǎn)品列表中使用 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, dispatch] = useReducer(uniqueReducer, []) 啟用 uniqueReducer。names 是保存產(chǎn)品名稱(chēng)的狀態(tài)變量,而 dispatch 是使用操作對象調用的函數。
當單擊 Add 按鈕時(shí),處理程序將調用 dispatch({ type: 'add', name: newName })。調度一個(gè) add 動(dòng)作使 reducer uniqueReducer 向狀態(tài)添加一個(gè)新的產(chǎn)品名稱(chēng)。
以同樣的方式,當單擊 Delete 按鈕時(shí),處理程序將調用 dispatch({ type: 'delete', name })。remove 操作將產(chǎn)品名稱(chēng)從名稱(chēng)狀態(tài)中刪除。
有趣的是,reducer 是命令模式的特例。
狀態(tài)變量應只關(guān)注一個(gè)點(diǎn)。
如果狀態(tài)具有復雜的更新邏輯,則將該邏輯從組件提取到自定義 hook 中。
同樣,如果狀態(tài)需要多個(gè)操作,請用 reducer 合并這些操作。
無(wú)論你使用什么規則,狀態(tài)都應該盡可能地簡(jiǎn)單和分離。組件不應被狀態(tài)更新的細節所困擾:它們應該是自定義 hook 或化簡(jiǎn)器的一部分。
這 3 個(gè)簡(jiǎn)單的規則能夠使你的狀態(tài)邏輯易于理解、維護和測試。
到此這篇關(guān)于React狀態(tài)管理的三個(gè)規則的文章就介紹到這了,更多相關(guān)React狀態(tài)管理內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。
Copyright ? 2009-2022 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 特網(wǎng)科技 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 百度云 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站