react重点和难点(关于React状态管理的三个规则总结)
react重点和难点
关于React状态管理的三个规则总结目录
- 前言
- No.1 一个关注点
- No.2 提取复杂的状态逻辑
- No.3 提取多个状态操作
- 总结
React 组件内部的状态是在渲染过程之间保持不变的封装数据。useState() 是 React hook,负责管理功能组件内部的状态。
我喜欢 useState() ,它确实使状态处理变得非常容易。但是我经常遇到类似的问题:
- 我应该将组件的状态划分为小状态,还是保持复合状态?
- 如果状态管理变得复杂,我应该从组件中提取它吗?该怎么做?
- 如果 useState() 的用法是如此简单,那么什么时候需要 useReducer()?
本文介绍了 3 条简单的规则,可以回答上述问题,并帮助你设计组件的状态。
No.1 一个关注点有效状态管理的第一个规则是:
使状态变量负责一个问题。
使状态变量负责一个问题使其符合单一责任原则。
让我们来看一个复合状态的示例,即一种包含多个状态值的状态。
const [state, setState] = useState({ on: true, count: 0 }); state.on // => true state.count // => 0
状态由一个普通的 JavaScript 对象组成,该对象具有 on 和 count 属性。
第一个属性 state.on 包含一个布尔值,表示开关。同样,``state.count` 包含一个表示计数器的数字,例如,用户单击按钮的次数。
然后,假设你要将计数器加1:
// Updating compound state setUser({ ...state, count: state.count + 1 });
你必须将整个状态放在一起,才能仅更新 count。这是为了简单地增加一个计数器而调用的一个大结构:这都是因为状态变量负责两个方面:开关和计数器。
解决方案是将复合状态分为两个原子状态 on 和 count:
const [on, setOnOff] = useState(true); const [count, setCount] = useState(0);
状态变量 on 仅负责存储开关状态。同样,count 变量仅负责计数器。
现在,让我们尝试更新计数器:
setCount(count + 1); // or using a callback setCount(count => count + 1);
count 状态仅负责计数,很容易推断,也很容易更新和读取。
不必担心调用多个 useState() 为每个关注点创建状态变量。
但是请注意,如果你使用过多的 useState() 变量,则你的组件很有可能就违反了“单一职责原则”。只需将此类组件拆分为较小的组件即可。
No.2 提取复杂的状态逻辑将复杂的状态逻辑提取到自定义 hook 中。
在组件内保留复杂的状态操作是否有意义?
答案来自基本面(通常会发生这种情况)。
创建 React hook 是为了将组件与复杂状态管理和副作用隔离开。因此,由于组件只应关注要渲染的元素和要附加的某些事件侦听器,所以应该把复杂的状态逻辑提取到自定义 hook 中。
考虑一个管理产品列表的组件。用户可以添加新的产品名称。约束是产品名称必须是唯一的。
第一次尝试是将产品名称列表的设置程序直接保留在组件内部:
function ProductsList() { const [names, setNames] = useState([]); const [newName, setNewName] = useState(''); const map = name => <li>{name}</li>; const handleChange = event => setNewName(event.target.value); const handleAdd = () => { const s = new Set([...names, newName]); setNames([...s]); }; return ( <li className="products"> {names.map(map)} <input type="text" onChange={handleChange} /> <button onClick={handleAdd}>Add</button> </li> ); }
names 状态变量保存产品名称。单击 Add 按钮时,将调用 addNewProduct() 事件处理程序。
在 addNewProduct() 内部,用 Set 对象来保持产品名称唯一。组件是否应该关注这个实现细节?不需要。
最好将复杂的状态设置器逻辑隔离到一个自定义 hook 中。开始做吧。
新的自定义钩子 useUnique() 可使每个项目保持唯一性:
// useUnique.js export function useUnique(initial) { const [items, setItems] = useState(initial); const add = newItem => { const uniqueItems = [...new Set([...items, newItem])]; setItems(uniqueItems); }; return [items, add]; };
将自定义状态管理提取到一个 hook 中后,ProductsList 组件将变得更加轻巧:
import { useUnique } from './useUnique'; function ProductsList() { const [names, add] = useUnique([]); const [newName, setNewName] = useState(''); const map = name => <li>{name}</li>; const handleChange = event => setNewName(e.target.value); const handleAdd = () => add(newName); return ( <li className="products"> {names.map(map)} <input type="text" onChange={handleChange} /> <button onClick={handleAdd}>Add</button> </li> ); }
const [names, addName] = useUnique([]) 启用自定义 hook。该组件不再被复杂的状态管理所困扰。
如果你想在列表中添加新名称,则只需调用 add('New Product Name') 即可。
最重要的是,将复杂的状态管理提取到自定义 hooks 中的好处是:
- 该组件不再包含状态管理的详细信息
- 自定义 hook 可以重复使用
- 自定义 hook 可轻松进行隔离测试
将多个状态操作提取到化简器中。
继续用 ProductsList 的例子,让我们引入“delete”操作,该操作将从列表中删除产品名称。
现在,你必须为 2 个操作编码:添加和删除产品。处理这些操作,就可以创建一个简化器并使组件摆脱状态管理逻辑。
同样,此方法符合 hook 的思路:从组件中提取复杂的状态管理。
以下是添加和删除产品的 reducer 的一种实现:
function uniqueReducer(state, action) { switch (action.type) { case 'add': return [...new Set([...state, action.name])]; case 'delete': return state.filter(name => name === action.name); default: throw new Error(); } }
然后,可以通过调用 React 的 useReducer() hook 在产品列表中使用 uniqueReducer():
function ProductsList() { const [names, dispatch] = useReducer(uniqueReducer, []); const [newName, setNewName] = useState(''); const handleChange = event => setNewName(event.target.value); const handleAdd = () => dispatch({ type: 'add', name: newName }); const map = name => { const delete = () => dispatch({ type: 'delete', name }); return ( <li> {name} <button onClick={delete}>Delete</button> </li> ); } return ( <li className="products"> {names.map(map)} <input type="text" onChange={handleChange} /> <button onClick={handleAdd}>Add</button> </li> ); }
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状态管理内容请搜索开心学习网以前的文章或继续浏览下面的相关文章希望大家以后多多支持开心学习网!
- react组件封装成函数方法(React虚拟列表的实现)
- vscode react jsx语法 开发环境(React-vscode使用jsx语法的问题及解决方法)
- react常用组件及作用(React中的Context应用场景分析)
- reactmap给了key仍然提示错误(react为什么不推荐使用index作为key)
- vue3.0 如何使用useroute(详解vue3中setUp和reactive函数的用法)
- react native常用组件(react native环境安装流程)
- html5创作(HTML5录音实践总结Preact)
- vue中的ref(Vue3.0中Ref与Reactive的区别示例详析)
- react 封装下拉选择框(React鼠标多选功能的配置方法)
- vue3 props用法(vue3组合API中setup、 ref、reactive的使用大全)
- react组件的参数怎样定义的(详解React中组件之间通信的方式)
- reactnative示例代码(React Native项目框架搭建的一些心得体会)
- react怎么添加动态html(react中的DOM操作实现)
- react表单组件怎么写(react antd实现动态增减表单)
- react native web白屏(关于React Native 无法链接模拟器的问题)
- react和antd项目教程(React引入antd-mobile+postcss搭建移动端)
- 虐待儿童是发泄支配欲的愚蠢行为(虐待儿童是发泄支配欲的愚蠢行为)
- 你或许不知道你隐藏的支配欲望(你或许不知道你隐藏的支配欲望)
- 把宽体丰田86卖了,换成7.5代高尔夫GTI玩起姿态与性能并存的改装(把宽体丰田86卖了)
- 大众推出了第五代高尔夫GT(大众推出了第五代高尔夫GT)
- 换代在即,现在是抄底 7.5代 高尔夫的最佳时机吗(换代在即现在是抄底)
- 2020年大众7.5代高尔夫R终结特别版 最后的呐喊(2020年大众7.5代高尔夫R终结特别版)
热门推荐
- jquery修改带有!important的样式
- 怎么样才知道云服务器够不够用(新手应该怎么选择云服务器才能避免被坑?)
- filezilla服务器支持断点续传吗(Filezilla Server配置FTP服务器提示操作超时的解决办法)
- VPS主机如何预防挂马?(VPS主机如何预防挂马?)
- js如何操作json字符串
- Sql Server 更新锁(UPDLOCK)
- html5的语法变化(详解HTML5.2版本带来的修改)
- phpcurl缺点(PHP封装cURL工具类与应用示例)
- 用php递归函数实现阶乘的计算(php求斐波那契数的两种实现方式递归与递推)
- python拖动选择文件操作(python通过paramiko复制远程文件及文件目录到本地)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9