React中对比无ID食谱数组并深拷贝更新书签状态的实现方法
现有实现的核心问题
- 匹配逻辑硬绑数组索引:当前代码按循环下标对位判断label,只要两个数组的存储顺序不一致,就会出现错配,完全不满足“通过label字段独立匹配”的要求
- 存在状态突变:代码直接给原数组的对象属性赋值,修改了原始
recipes的对象引用,是React开发中需要规避的反模式
实现方案
- 先提取
localRecipes中所有食谱的label值,存入Set结构,实现O(1)复杂度的快速匹配 - 遍历服务端返回的
recipes数组,对每一个条目做全层级的对象拷贝,断开和原始对象的引用关联 - 对拷贝后的新条目做匹配判断:如果其label存在于本地Set中,就把新条目的
bookmarked属性设为true,否则保留原有属性值 - 将所有处理后的新条目收集为全新数组,后续绑定到对应state即可,全程不会修改原始
recipes数组
可直接运行的参考代码
useEffect(() => { // 提前做存在性校验,避免空值报错 if (!recipes?.length || !localRecipes?.length) return; // 生成本地食谱的label集合,消除嵌套循环 const localRecipeLabels = new Set( localRecipes.map(localItem => localItem.recipe.label) ); // 遍历生成深拷贝后的新数组 const mergedRecipes = recipes.map(serverItem => { // 针对当前结构做两层拷贝,断开引用 const copiedItem = { ...serverItem, recipe: { ...serverItem.recipe } }; // 匹配到本地记录时更新bookmarked状态 if (localRecipeLabels.has(copiedItem.recipe.label)) { copiedItem.bookmarked = true; } return copiedItem; }); // 将结果更新到对应state中即可 // setRecipes(mergedRecipes) }, [isFetching, recipes, localRecipes]);
补充说明
- 如果食谱对象存在更深层的嵌套字段,可以替换成通用深拷贝方法,避免浅拷贝带来的引用共享问题
- 原代码的依赖数组仅写了
isFetching,容易因为闭包拿到旧的recipes、localRecipes值,需要补全为实际用到的响应式变量 - Set结构的匹配逻辑不受数组顺序影响,只要label值一致就能正确匹配,不会出现原代码的顺序依赖问题
内容的提问来源于stack exchange,提问作者Tony Melnichuk
相关产品推荐
相关产品推荐

