简体   繁体   English

如何使用 useState 添加、更新或删除嵌套对象

[英]How to add, update or remove nested objects with useState

I have a possible infinite category tree and I would like to add, update or remove categories at any level with setState in react.我有一个可能的无限类别树,我想使用 setState 在任何级别添加、更新或删除类别。 I know this is possible with recursion but I don't have enough experience to manage this problem on my own.我知道这可以通过递归实现,但我没有足够的经验来自己解决这个问题。 Here is how the data could possible look like:以下是数据可能的样子:

const categories = [
  {
    id: "1",
    name: "category1",
    subCategories: [
      {
        id: "sub1",
        name: "subcategory1",
        subCategories: [
          { id: "subsub1", name: "subsubcategory1", subCategories: [] },
          { id: "subsub2", name: "subsubcategory2", subCategories: [] }
        ]
      },
      { id: "sub2", name: "subcategory2", subCategories: [] }
    ]
  },
  {
    id: "2",
    name: "category2",
    subCategories: []
  }
]

Considering that your top level categories object is an object and not an array, the add and remove function could be the following (same pattern for update)考虑到您的顶级类别对象是对象而不是数组,添加和删除函数可能如下(更新模式相同)

function add (tree, newCategory, parentId) {
    if(tree.id === parentId) 
      return {
        ...tree,
        subCategories: tree.subCategories.concat(newCategory)
      }
    return {
        ...tree, 
        subCategories: tree.subCategories.map(c => add(c, newCategory, parentId))
    }
}



function remove (tree, idToRemove) {
        if(tree.subCategories.map(c => c.id).includes(idToRemove)) 
          return {
            ...tree,
            subCategories: tree.subCategories.filter(c => c.id !== idToRemove)
          }
        return {
            ...tree, 
            subCategories: tree.subCategories.map(c => remove(c, idToRemove))
        }
    }

this is how the recursive function would look like.这就是递归函数的样子。 the arguments:论据:

id: id to look for id:要查找的id

cats: the categories array to loop猫:要循环的类别数组

nestSubCategory: (boolean) if we want to add the subcategory object in the subCategories array or not nestSubCategory: (boolean) 是否要在 subCategories 数组中添加子类别对象

subCategory: the category object we want to insert subCategory:我们要插入的类别对象

const addCategories = (id, cats, nestSubCategory, subCategory)=> {
  const cat = cats.find(item=> item.id === id)
  
  const arrSubs = cats.filter(item => item.subCategories?.length)
  .map(item => item.subCategories)
 
  if(cat){
    if(nestSubCategory){
       cat.subCategories.push(subCategory)
       return
    }else{
       cats.push(subCategory)
       return
    }
  }
  
  else{
    return addCategories(id, arrSubs[0], nestSubCategory, subCategory)
  }
    
}
  

addCategories("blabla1", categories, true,  { id: "blabla2", name: "blablacategory1", subCategories: [] })
  //console.log(categories)
console.log(
  JSON.stringify(categories)
  )

remember to update the object in the state replacing the entire categories array once the function is executed.记住在函数执行后更新处于替换整个类别数组的状态的对象。 be careful with recursion 🖖🏽小心递归🖖🏽

you can do in a similar way to remove items, i leave to you the pleasure to experiment您可以以类似的方式删除项目,我留给您尝试的乐趣

Prologue序幕

To update a nested property in an immutable way, you need to copy or perform immutable operations on all its parents.要以不可变的方式更新嵌套属性,您需要在其所有父级上复制或执行不可变操作。

Setting a property on a nested object:在嵌套对象上设置属性:

return {
  ...parent,
  person: {
    ...parent.person,
    name: 'New Name'
  }
}

Arrays: You may pre-clone the array, or use a combination of .slice() , .map() , .filter() and the concatenation operator ( ... );数组:您可以预先克隆数组,或使用.slice().map().filter()和连接运算符 ( ... ) 的组合; Warning: .splice mutates the array.警告: .splice 会改变数组。

(this can be a long topic, so I just gave a veryfast overview) (这可能是一个很长的话题,所以我只是给出了一个非常快速的概述)

immer沉浸

As this can quickly get very ugly on objects with deep nesting, using the immer lib quite becomes a must at some point.由于这在具有深度嵌套的对象上很快就会变得非常难看,因此在某些时候使用immer库非常必要。 Immer "creates new immutable state from mutations". Immer “从突变中创建新的不可变状态”。

const newState = immer(oldState, draft => {
  draft[1].subcategories[3].subcategories[1].name = 'Category'
})

In the extreme case, you can combine immer with lodash to create mutations in arbitrary places:在极端情况下,您可以将 immer 与 lodash 结合起来在任意位置创建突变:

import set from 'lodash/set'
const newState = immer(oldState, draft => {
  set(draft, [0, 'subcategories', 5, 'subcategories', 3], { id: 5, name:'Cat' })
})

"You might not need lodash" website has the recursive implementation for lodash/set. “你可能不需要 lodash”网站有 lodash/set 的递归实现。 But, seriously, just use lodash.但是,说真的,只需使用 lodash。

PLUSES:优点:

  • If you are using redux-toolkit, immer already is auto-applied on reducers, and is exposed as createNextState (check docs with care).如果您使用的是 redux-toolkit,则 immer 已经自动应用于 reducer,并以createNextState的形式公开(请仔细查看文档)。
  • Deeply nested state can be an interesting use case for normalizr (long talk).深度嵌套状态可能是normalizr (长话)的一个有趣用例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM