React?state結(jié)構(gòu)設(shè)計原則示例詳解
React State 結(jié)構(gòu)設(shè)計
React 中組件 state 狀態(tài)管理是組件設(shè)計中的難點之一,如何設(shè)計state的結(jié)構(gòu)。遵循以下原則可以保障state更新不出現(xiàn)邏輯上的錯誤,也可以避免不必要的 state 維護:
相關(guān)的狀態(tài)組合成一個group
當每次觸發(fā)更新的時候需要更新兩個state 則這兩個state可以嘗試合并成一個state【從單個值類型,變成object 或者 Array 等類型】。
import { useState } from 'react'; function ComA() { // bad case const [x, setX] = useState<number>(0); const [y, setY] = useState<number>(0); // good case const [position, setPosition] = useState({ x: 0, y: 0 }); return ( <div> <div style={{ position: 'absolute', backgroundColor: 'red', borderRadius: '50%', transform: `translate(${x}px, ${y}px)`, left: -10, top: -10, width: 20, height: 20, }} /> </div> ); }
避免出現(xiàn)競態(tài)的state
也就是說兩個或多個 state 存在競態(tài),同一時刻有且僅有一個是真值。如果存在這種問題,則需要考慮避免當前這種state的結(jié)構(gòu), 使用不同的值去區(qū)分沖突的 state,這樣就把多個沖突的state 合并成1個state,區(qū)別在于value的變化以及其代表的意義。
import { useState } from 'react'; // bad case function ComA() { // 表示編輯狀態(tài) const [isWritting, setIsWritting] = useState(true); // 表示是否保存 const [isSave, setIsSave] = useState(fasle); // 其他狀態(tài) const [isComplete, setIsComplete] = useState(false); return ( //... ); } // 這中間 isWriting 和 isSave 是沖突的。也就是說兩個state存在競態(tài),有且僅有一個是真值。 // combine mutilate state ingroup function ComB() { const [status, setStatus] = useState<'writing' | 'save' | 'complete'>('writing'); return ( // ... ); }
避免多余的state
如果一個 state 可以通過其他 state 的計算得出【.length, 取反異或等】,那么這個 state 就是不需要存在的。
export default function Form() { const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); const [fullName, setFullName] = useState(''); function handleFirstNameChange(e) { setFirstName(e.target.value); setFullName(e.target.value + ' ' + lastName); } function handleLastNameChange(e) { setLastName(e.target.value); setFullName(firstName + ' ' + e.target.value); } return ( <> <h2>Let's check you in</h2> <label> First name:{' '} <input value={firstName} onChange={handleFirstNameChange} /> </label> <label> Last name:{' '} <input value={lastName} onChange={handleLastNameChange} /> </label> <p> Your ticket will be issued to: <b>{fullName}</b> </p> </> ); } // fullname 完全可以由 firstName 和 lastName 拼接出來,使用單獨的 state 來保存計算結(jié)果是多余的。
避免重復的狀態(tài)
如果state存在重復相同的數(shù)據(jù)時,這部分重復的數(shù)據(jù)很難保持同步更新?!疽话闶轻槍?shù)組項的處理,data 保存在一個state 中,然后又使用一個state保存選中或者編輯某項。這時候data中的數(shù)據(jù)更新,current 可能會被緩存到舊值】。需要避免這種重復。解決辦法【避免保存重復的內(nèi)容,而是保存找到指定數(shù)據(jù)的id或者索引】。
import { useState } from 'react'; const defaultData = [ { title: 'Tom', id: 0 }, { title: 'Sam', id: 1 }, { title: 'Dodo', id: 2 }, { title: 'Piker', id: 3 }, ]; function ComA() { const [data, setData] = useState(defaultData); const [current, setCurrent] = useState(data[0]); function handleClick(item) { setCurrent(item); } function handleInput(id, value) { const newData = data.map((item)=>{ if (id === item.id) { return { ...item, title: value }; } return item; }) } return ( <> <ul> { data.map((item) => { return ( <li key={item.id}> <input value={item.title} onChange= {({target})=>{ handleInput(item.id, target.value); }}/> <button onClick={()=>{ handleClick(item) }}>選中</button> </li> ); }) } </ul> <p>當前選中:{current.title}</p> </> ); } // 問題: 當點擊 “選中” 按鈕后, current 保存了當前 item 的一個引用。接著編輯當前項的title,發(fā)現(xiàn)并不會同步到<p>中展示。
解決方法1:
細心檢查代碼能看出來,通過 handleInput 執(zhí)行時,返回了新的對象更新 data 中的 item。只要稍微修改一下handleInput的代碼,同時更新current即可。
function handleInput(id, value) { const newData = data.map((item)=>{ if (id === item.id) { // return { ...item, title: value }; const newItem = { ...item, title: value }; setCurrent(newItem); return newItem; } return item; }) }
但是這種方式不能一勞永逸,其他函數(shù)中再修改其他屬性數(shù)據(jù),還得增加同樣的邏輯。
解決方法2:
保存 item 的 id 不要保存重復的數(shù)據(jù)內(nèi)容。
import { useState } from 'react'; const defaultData = [ { title: 'Tom', id: 0 }, { title: 'Sam', id: 1 }, { title: 'Dodo', id: 2 }, { title: 'Piker', id: 3 }, ]; function ComA() { const [data, setData] = useState(defaultData); // 修改 const [currentId, setCurrentId] = useState(0); const currentItem = data.find(({id}) => id === currentId); function handleClick({id}) { setCurrentId(id); } function handleInput(id, value) { const newData = data.map((item)=>{ if (id === item.id) { return { ...item, title: value }; } return item; }) } return ( <> <ul> { data.map((item) => { return ( <li key={item.id}> <input value={item.title} onChange= {({target})=>{ handleInput(item.id, target.value); }}/> <button onClick={()=>{ handleClick(item) }}>選中</button> </li> ); }) } </ul> <p>當前選中:{current.title}</p> </> ); }
一勞永逸解決問題,保存id。更新的時候組件會自動獲取對應的數(shù)據(jù)項
避免出現(xiàn)過深的嵌套state
深度嵌套的state不便于更新,更新時,需要一層一層的解構(gòu),重組成新的嵌套對象。如果可以嘗試使用平鋪的方式組織state結(jié)構(gòu)。react 進行state更新時,引用類型數(shù)據(jù)需要使用新的引用結(jié)構(gòu)進行更新【解構(gòu)復制,修改對應value】,如果嵌套層級過多,更新時解構(gòu)層級越復雜,容易出問題。
以這些原則作為 state 結(jié)構(gòu)設(shè)計方法論,逐步實現(xiàn)性感&合理的 React 組件!
更多關(guān)于React state 結(jié)構(gòu)設(shè)計原則的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
TS裝飾器bindThis優(yōu)雅實現(xiàn)React類組件中this綁定
這篇文章主要為大家介紹了TS裝飾器bindThis優(yōu)雅實現(xiàn)React類組件中this綁定,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11React18?useState何時執(zhí)行更新及微任務(wù)理解
這篇文章主要為大家介紹了React18?useState何時執(zhí)行更新及微任務(wù)理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11關(guān)于React狀態(tài)管理的三個規(guī)則總結(jié)
隨著 JavaScript 單頁應用開發(fā)日趨復雜,JavaScript 需要管理比任何時候都要多的 state (狀態(tài)),這篇文章主要給大家介紹了關(guān)于React狀態(tài)管理的三個規(guī)則,需要的朋友可以參考下2021-07-07Redis數(shù)據(jù)結(jié)構(gòu)面試高頻問題解析
這篇文章主要為大家介紹了Redis數(shù)據(jù)結(jié)構(gòu)高頻面試問題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06react-redux集中式狀態(tài)管理及基本使用與優(yōu)化
react-redux把組件分為兩類,一類叫做UI組件,一類叫做容器組件,這篇文章主要介紹了集中式狀態(tài)管理<react-redux>基本使用與優(yōu)化,需要的朋友可以參考下2022-08-08React實現(xiàn)基于Antd密碼強度校驗組件示例詳解
這篇文章主要為大家介紹了React實現(xiàn)基于Antd密碼強度校驗組件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-01-01