React state结构设计原则示例详解
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<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> ); }
避免出现竞态的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' | '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 来保存计算结果是多余的。
避免重复的状态
如果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 ( <> <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,发现并不会同步到<p>中展示。
解决方法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 ( <> <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。更新的时候组件会自动获取对应的数据项
避免出现过深的嵌套state
深度嵌套的state不便于更新,更新时,需要一层一层的解构,重组成新的嵌套对象。如果可以尝试使用平铺的方式组织state结构。react 进行state更新时,引用类型数据需要使用新的引用结构进行更新【解构复制,修改对应value】,如果嵌套层级过多,更新时解构层级越复杂,容易出问题。
以这些原则作为 state 结构设计方法论,逐步实现性感&合理的 React 组件!
更多关于React state 结构设计原则的资料请关注脚本之家其它相关文章!
相关文章
react ant-design Select组件下拉框map不显示的解决
这篇文章主要介绍了react ant-design Select组件下拉框map不显示的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2024-03-03
最新评论