• 售前

  • 售后

热门帖子
入门百科

关于React状态管理的三个规则总结

[复制链接]
123456811 显示全部楼层 发表于 2021-8-14 13:50:51 |阅读模式 打印 上一主题 下一主题
目次


  • 前言
  • No.1 一个关注点
  • No.2 提取复杂的状态逻辑
  • No.3 提取多个状态操作
  • 总结

前言

React 组件内部的状态是在渲染过程之间保持稳定的封装数据。useState() 是 React hook,负责管理功能组件内部的状态。
我喜好 useState() ,它确实使状态处置惩罚变得非常容易。但是我经常遇到类似的问题:
       
  • 我应该将组件的状态划分为小状态,照旧保持复合状态?   
  • 假如状态管理变得复杂,我应该从组件中提取它吗?该怎么做?   
  • 假如 useState() 的用法是如此简朴,那么什么时间需要 useReducer()?
本文介绍了 3 条简朴的规则,可以回答上述问题,并资助你计划组件的状态。

No.1 一个关注点


有用状态管理的第一个规则是:
  1. 使状态变量负责一个问题。
复制代码
使状态变量负责一个问题使其符合单一责任原则。
让我们来看一个复合状态的示例,即一种包罗多个状态值的状态。
  1. const [state, setState] = useState({
  2.     on: true,
  3.     count: 0
  4. });
  5. state.on    // => true
  6. state.count // => 0
复制代码
状态由一个平凡的 JavaScript 对象构成,该对象具有 on 和 count 属性。
第一个属性 state.on 包罗一个布尔值,表现开关。同样,``state.count` 包罗一个表现计数器的数字,比方,用户单击按钮的次数。
然后,假设你要将计数器加1:
  1. // Updating compound state
  2. setUser({
  3.     ...state,
  4.     count: state.count + 1
  5. });
复制代码
你必须将整个状态放在一起,才能仅更新 count。这是为了简朴地增加一个计数器而调用的一个大结构:这都是因为状态变量负责两个方面:开关和计数器。
办理方案是将复合状态分为两个原子状态 on 和 count:
  1. const [on, setOnOff] = useState(true);
  2. const [count, setCount] = useState(0);
复制代码
状态变量 on 仅负责存储开关状态。同样,count 变量仅负责计数器。
现在,让我们尝试更新计数器:
  1. setCount(count + 1);
  2. // or using a callback
  3. setCount(count => count + 1);
复制代码
count 状态仅负责计数,很容易推断,也很容易更新和读取。
不必担心调用多个 useState() 为每个关注点创建状态变量。
但是请注意,假如你利用过多的 useState() 变量,则你的组件很有大概就违背了“单一职责原则”。只需将此类组件拆分为较小的组件即可。

No.2 提取复杂的状态逻辑

  1. 将复杂的状态逻辑提取到自定义 hook 中。
复制代码
在组件内保留复杂的状态操作是否故意义?
答案来自根本面(通常会发生这种情况)。
创建 React hook 是为了将组件与复杂状态管理和副作用隔脱离。因此,由于组件只应关注要渲染的元素和要附加的某些变乱侦听器,以是应该把复杂的状态逻辑提取到自定义 hook 中。
考虑一个管理产品列表的组件。用户可以添加新的产品名称。约束是产品名称必须是唯一的。
第一次尝试是将产品名称列表的设置步调直接保留在组件内部:
  1. function ProductsList() {
  2.     const [names, setNames] = useState([]);  
  3.     const [newName, setNewName] = useState('');
  4.     const map = name => <div>{name}</div>;
  5.     const handleChange = event => setNewName(event.target.value);
  6.     const handleAdd = () => {   
  7.         const s = new Set([...names, newName]);   
  8.         setNames([...s]);  };
  9.     return (
  10.         <div className="products">
  11.             {names.map(map)}
  12.             <input type="text" onChange={handleChange} />
  13.             <button onClick={handleAdd}>Add</button>
  14.         </div>
  15.     );
  16. }
复制代码
names 状态变量保存产品名称。单击 Add 按钮时,将调用 addNewProduct() 变乱处置惩罚步调。
在 addNewProduct() 内部,用 Set 对象来保持产品名称唯一。组件是否应该关注这个实现细节?不需要。
最好将复杂的状态设置器逻辑隔离到一个自定义 hook 中。开始做吧。
新的自定义钩子 useUnique() 可使每个项目保持唯一性:
  1. // useUnique.js
  2. export function useUnique(initial) {
  3.     const [items, setItems] = useState(initial);
  4.     const add = newItem => {
  5.         const uniqueItems = [...new Set([...items, newItem])];
  6.         setItems(uniqueItems);
  7.     };
  8.     return [items, add];
  9. };
复制代码
将自定义状态管理提取到一个 hook 中后,ProductsList 组件将变得更加轻巧:
  1. import { useUnique } from './useUnique';
  2. function ProductsList() {
  3.   const [names, add] = useUnique([]);  const [newName, setNewName] = useState('');
  4.   const map = name => <div>{name}</div>;
  5.   const handleChange = event => setNewName(e.target.value);
  6.   const handleAdd = () => add(newName);
  7.   return (
  8.     <div className="products">
  9.       {names.map(map)}
  10.       <input type="text" onChange={handleChange} />
  11.       <button onClick={handleAdd}>Add</button>
  12.     </div>
  13.   );
  14. }
复制代码
const [names, addName] = useUnique([]) 启用自定义 hook。该组件不再被复杂的状态管理所困扰。
假如你想在列表中添加新名称,则只需调用 add('New Product Name') 即可。
最告急的是,将复杂的状态管理提取到自定义 hooks 中的长处是:
       
  • 该组件不再包罗状态管理的具体信息   
  • 自定义 hook 可以重复利用   
  • 自定义 hook 可轻松进行隔离测试

No.3 提取多个状态操作

  1. 将多个状态操作提取到化简器中。
复制代码
继承用 ProductsList 的例子,让我们引入“delete”操作,该操作将从列表中删除产品名称。
现在,你必须为 2 个操作编码:添加和删除产品。处置惩罚这些操作,就可以创建一个简化器并使组件摆脱状态管理逻辑。
同样,此方法符合 hook 的思绪:从组件中提取复杂的状态管理。
以下是添加和删除产品的 reducer 的一种实现:
  1. function uniqueReducer(state, action) {
  2.     switch (action.type) {
  3.         case 'add':
  4.             return [...new Set([...state, action.name])];
  5.         case 'delete':
  6.             return state.filter(name => name === action.name);
  7.         default:
  8.             throw new Error();
  9.     }
  10. }
复制代码
然后,可以通过调用 React 的 useReducer()  hook 在产品列表中利用 uniqueReducer():
  1. function ProductsList() {
  2.     const [names, dispatch] = useReducer(uniqueReducer, []);
  3.     const [newName, setNewName] = useState('');
  4.     const handleChange = event => setNewName(event.target.value);
  5.     const handleAdd = () => dispatch({ type: 'add', name: newName });
  6.     const map = name => {
  7.         const delete = () => dispatch({ type: 'delete', name });   
  8.         return (
  9.             <div>
  10.                 {name}
  11.                 <button onClick={delete}>Delete</button>
  12.             </div>
  13.         );
  14.     }
  15.     return (
  16.         <div className="products">
  17.             {names.map(map)}
  18.             <input type="text" onChange={handleChange} />
  19.             <button onClick={handleAdd}>Add</button>
  20.         </div>
  21.     );
  22. }
复制代码
const [names, dispatch] = useReducer(uniqueReducer, []) 启用 uniqueReducer。names 是保存产品名称的状态变量,而 dispatch 是利用操尴尬刁难象调用的函数。
当单击 Add 按钮时,处置惩罚步调将调用 dispatch({ type: 'add', name: newName })。调度一个 add 动作使 reducer uniqueReducer 向状态添加一个新的产品名称。
以同样的方式,当单击 Delete 按钮时,处置惩罚步调将调用 dispatch({ type: 'delete', name })。remove 操作将产品名称从名称状态中删除。
风趣的是,reducer 是下令模式的特例。

总结


状态变量应只关注一个点。
假如状态具有复杂的更新逻辑,则将该逻辑从组件提取到自定义 hook 中。
同样,假如状态需要多个操作,请用 reducer 归并这些操作。
无论你利用什么规则,状态都应该尽大概地简朴和分离。组件不应被状态更新的细节所困扰:它们应该是自定义 hook 或化简器的一部分。
这 3 个简朴的规则能够使你的状态逻辑易于理解、维护和测试。
到此这篇关于React状态管理的三个规则的文章就介绍到这了,更多相关React状态管理内容请搜索脚本之家从前的文章或继承欣赏下面的相关文章盼望各人以后多多支持脚本之家!

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作