React State 结构设计
React 中组件 state 状态管理是组件设计中的难点之一,如何设计state的结构。遵循以下原则可以保障state更新不出现逻辑上的错误,也可以避免不必要的 state 维护:
- 相关的状态组合成一个group。当每次触发更新的时候需要更新两个state 则这两个state可以尝试合并成一个state【从单个值类型,变成object 或者 Array 等类型】。
import { useState } from 'react';
function ComA() {
// bad case
const [x, setX] = useState(0);
const [y, setY] = useState(0);
// good case
const [position, setPosition] = useState({ x: 0, y: 0 });
return (
);
}
- 避免出现竞态的state, 也就是说两个或多个 state 存在竞态,同一时刻有且仅有一个是真值。如果存在这种问题,则需要考虑避免当前这种state的结构, 使用不同的值去区分冲突的 state,这样就把多个冲突的state 合并成1个state,区别在于value的变化以及其代表的意义。
import { useState } from 'react';
// bad case
function ComA() {
// 表示编辑状态
const [isWritting, setIsWritting] = useState(true);
// 表示是否保存
const [isSave, setIsSave] = useState(fasle);
// 其他状态
const [isComplete, setIsComplete] = useState(false);
return (
//...
);
}
// 这中间 isWriting 和 isSave 是冲突的。也就是说两个state存在竞态,有且仅有一个是真值。
// combine mutilate state ingroup
function ComB() {
const [status, setStatus] = useState('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 (
Let’s check you in
Your ticket will be issued to: {fullName}
>
);
}
// fullname 完全可以由 firstName 和 lastName 拼接出来,使用单独的 state 来保存计算结果是多余的。
- 避免重复的状态。如果state存在重复相同的数据时,这部分重复的数据很难保持同步更新。【一般是针对数组项的处理,data 保存在一个state 中,然后又使用一个state保存选中或者编辑某项。这时候data中的数据更新,current 可能会被缓存到旧值】。需要避免这种重复。解决办法【避免保存重复的内容,而是保存找到指定数据的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 (
{
data.map((item) => {
return (
-
{ handleInput(item.id, target.value); }}/>
);
})
}
当前选中:{current.title}
>
);
}
// 问题: 当点击 “选中” 按钮后, current 保存了当前 item 的一个引用。接着编辑当前项的title,发现并不会同步到中展示。
解决方法1:
细心检查代码能看出来,通过 handleInput 执行时,返回了新的对象更新 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;
})
}
但是这种方式不能一劳永逸,其他函数中再修改其他属性数据,还得增加同样的逻辑。
解决方法2:保存 item 的 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 [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 (
{
data.map((item) => {
return (
-
{ handleInput(item.id, target.value); }}/>
);
})
}
当前选中:{current.title}
>
);
}
一劳永逸解决问题,保存id。更新的时候组件会自动获取对应的数据项
- 避免出现过深的嵌套state。深度嵌套的state不便于更新,更新时,需要一层一层的解构,重组成新的嵌套对象。如果可以尝试使用平铺的方式组织state结构。react 进行state更新时,引用类型数据需要使用新的引用结构进行更新【解构复制,修改对应value】,如果嵌套层级过多,更新时解构层级越复杂,容易出问题。
以这些原则作为 state 结构设计方法论,逐步实现性感&合理的 React 组件!
本文由mdnice多平台发布
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
嗨,朋友!你听说过「新型工伤」吗? 我好像「赛博确诊」了😣 那天朋友约我吃饭,我下意识回复了句「好的,那我提一个日程」……还有上次跟一位准妈妈聊天,我好奇宝宝的预产期,结果脱口而出「宝宝预计什么时候发布呀?」 小编观察到,这种生活语言系统被职场黑话污染的「新型…