将JS传统函数转为React useState可用的不可变函数及优化问询
React useState下数组元素添加/移除的不可变实现分析
需求背景
需要将以下经典JS数组操作函数,转换为React useState 组件中符合不可变原则的实现:功能为检查某条数据的id是否存在于数组中,存在则移除该数据,不存在则添加该数据。
经典函数实现:
const addOrRemove = (array, item) => { array.indexOf(item) === -1 ? array.push(item) : array.splice(array.indexOf(item), 1); }
用户自行编写的React实现:
array.indexOf(item) === -1 ? setArray(array=>[...array, item]) : setArray(array.filter(arrayitem=>arrayitem.id !== item.id)
一、现有实现的正确性分析
不可变性判断
你的实现核心逻辑符合不可变原则:
- 添加元素时使用扩展运算符
[...array, item]生成新数组,没有修改原状态数组; - 移除元素时使用
filter方法,该方法返回全新数组,同样不会修改原状态。
存在的问题
- 判断逻辑不准确:
原经典函数用array.indexOf(item)判断元素是否存在,但如果item是引用类型(对象),即使两个对象的id相同,由于内存引用不同,indexOf会返回-1,导致重复添加相同id的元素。你在移除时已经用id对比,但判断存在性时仍用indexOf,逻辑不一致。 - 语法错误:
移除分支的filter调用后缺少闭合括号,正确写法应为:setArray(array => array.filter(arrayitem => arrayitem.id !== item.id))
二、优化方案
针对上述问题,优化后的实现如下:
const toggleItem = (item) => { setArray(prevArray => { // 统一用id判断元素是否存在,避免引用类型的判断误差 const exists = prevArray.some(arrItem => arrItem.id === item.id); if (exists) { // 移除:返回过滤后的新数组 return prevArray.filter(arrItem => arrItem.id !== item.id); } else { // 添加:返回包含新元素的新数组 return [...prevArray, item]; } }); };
优化点说明:
- 统一使用
id判断元素是否存在,确保逻辑一致性; - 使用函数式更新
setArray(prevArray => ...),保证获取到最新的状态值,避免闭包导致的状态过期问题; - 封装为独立函数
toggleItem,提升代码复用性和可读性。
三、更优的实现方式(单次遍历版本)
上面的优化方案需要两次遍历数组(some+filter),当数组较大时,可改用findIndex实现单次遍历,性能更优:
const toggleItem = (item) => { setArray(prevArray => { const targetIndex = prevArray.findIndex(arrItem => arrItem.id === item.id); if (targetIndex === -1) { // 不存在则添加 return [...prevArray, item]; } else { // 存在则移除:通过slice切割数组生成新数组 return [...prevArray.slice(0, targetIndex), ...prevArray.slice(targetIndex + 1)]; } }); };
该方案通过findIndex一次遍历找到目标元素的索引,再根据索引判断是添加还是移除,只需要遍历数组一次,效率更高,同时保持了不可变性和可读性。
内容的提问来源于stack exchange,提问作者mdc123
相关产品推荐
相关产品推荐

