React?state結(jié)構(gòu)設(shè)計(jì)原則示例詳解
React State 結(jié)構(gòu)設(shè)計(jì)
React 中組件 state 狀態(tài)管理是組件設(shè)計(jì)中的難點(diǎn)之一,如何設(shè)計(jì)state的結(jié)構(gòu)。遵循以下原則可以保障state更新不出現(xiàn)邏輯上的錯(cuò)誤,也可以避免不必要的 state 維護(hù):
相關(guān)的狀態(tài)組合成一個(gè)group
當(dāng)每次觸發(fā)更新的時(shí)候需要更新兩個(gè)state 則這兩個(gè)state可以嘗試合并成一個(gè)state【從單個(gè)值類型,變成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)競(jìng)態(tài)的state
也就是說(shuō)兩個(gè)或多個(gè) state 存在競(jìng)態(tài),同一時(shí)刻有且僅有一個(gè)是真值。如果存在這種問題,則需要考慮避免當(dāng)前這種state的結(jié)構(gòu), 使用不同的值去區(qū)分沖突的 state,這樣就把多個(gè)沖突的state 合并成1個(gè)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 是沖突的。也就是說(shuō)兩個(gè)state存在競(jìng)態(tài),有且僅有一個(gè)是真值。
// combine mutilate state ingroup
function ComB() {
const [status, setStatus] = useState<'writing' | 'save' | 'complete'>('writing');
return (
// ...
);
}避免多余的state
如果一個(gè) state 可以通過(guò)其他 state 的計(jì)算得出【.length, 取反異或等】,那么這個(gè) 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 拼接出來(lái),使用單獨(dú)的 state 來(lái)保存計(jì)算結(jié)果是多余的。避免重復(fù)的狀態(tài)
如果state存在重復(fù)相同的數(shù)據(jù)時(shí),這部分重復(fù)的數(shù)據(jù)很難保持同步更新?!疽话闶轻槍?duì)數(shù)組項(xiàng)的處理,data 保存在一個(gè)state 中,然后又使用一個(gè)state保存選中或者編輯某項(xiàng)。這時(shí)候data中的數(shù)據(jù)更新,current 可能會(huì)被緩存到舊值】。需要避免這種重復(fù)。解決辦法【避免保存重復(fù)的內(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>當(dāng)前選中:{current.title}</p>
</>
);
}
// 問題: 當(dāng)點(diǎn)擊 “選中” 按鈕后, current 保存了當(dāng)前 item 的一個(gè)引用。接著編輯當(dāng)前項(xiàng)的title,發(fā)現(xiàn)并不會(huì)同步到<p>中展示。解決方法1:
細(xì)心檢查代碼能看出來(lái),通過(guò) handleInput 執(zhí)行時(shí),返回了新的對(duì)象更新 data 中的 item。只要稍微修改一下handleInput的代碼,同時(shí)更新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 不要保存重復(fù)的數(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>當(dāng)前選中:{current.title}</p>
</>
);
}一勞永逸解決問題,保存id。更新的時(shí)候組件會(huì)自動(dòng)獲取對(duì)應(yīng)的數(shù)據(jù)項(xiàng)
避免出現(xiàn)過(guò)深的嵌套state
深度嵌套的state不便于更新,更新時(shí),需要一層一層的解構(gòu),重組成新的嵌套對(duì)象。如果可以嘗試使用平鋪的方式組織state結(jié)構(gòu)。react 進(jìn)行state更新時(shí),引用類型數(shù)據(jù)需要使用新的引用結(jié)構(gòu)進(jìn)行更新【解構(gòu)復(fù)制,修改對(duì)應(yīng)value】,如果嵌套層級(jí)過(guò)多,更新時(shí)解構(gòu)層級(jí)越復(fù)雜,容易出問題。
以這些原則作為 state 結(jié)構(gòu)設(shè)計(jì)方法論,逐步實(shí)現(xiàn)性感&合理的 React 組件!
更多關(guān)于React state 結(jié)構(gòu)設(shè)計(jì)原則的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
TS裝飾器bindThis優(yōu)雅實(shí)現(xiàn)React類組件中this綁定
這篇文章主要為大家介紹了TS裝飾器bindThis優(yōu)雅實(shí)現(xiàn)React類組件中this綁定,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
React18?useState何時(shí)執(zhí)行更新及微任務(wù)理解
這篇文章主要為大家介紹了React18?useState何時(shí)執(zhí)行更新及微任務(wù)理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
關(guān)于React狀態(tài)管理的三個(gè)規(guī)則總結(jié)
隨著 JavaScript 單頁(yè)應(yīng)用開發(fā)日趨復(fù)雜,JavaScript 需要管理比任何時(shí)候都要多的 state (狀態(tài)),這篇文章主要給大家介紹了關(guān)于React狀態(tài)管理的三個(gè)規(guī)則,需要的朋友可以參考下2021-07-07
Redis數(shù)據(jù)結(jié)構(gòu)面試高頻問題解析
這篇文章主要為大家介紹了Redis數(shù)據(jù)結(jié)構(gòu)高頻面試問題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
react-redux集中式狀態(tài)管理及基本使用與優(yōu)化
react-redux把組件分為兩類,一類叫做UI組件,一類叫做容器組件,這篇文章主要介紹了集中式狀態(tài)管理<react-redux>基本使用與優(yōu)化,需要的朋友可以參考下2022-08-08
React實(shí)現(xiàn)基于Antd密碼強(qiáng)度校驗(yàn)組件示例詳解
這篇文章主要為大家介紹了React實(shí)現(xiàn)基于Antd密碼強(qiáng)度校驗(yàn)組件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
原生實(shí)現(xiàn)一個(gè)react-redux的代碼示例
這篇文章主要介紹了原生實(shí)現(xiàn)一個(gè)react-redux的代碼示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06

