Skip to content
Advertisement

Maintaining react state with a hierarchical object using react hooks (add or update)

I have an a state object in React that looks something like this (book/chapter/section/item):

  const book = {
     id: "123",
     name: "book1",
     chapters: [
      {
        id: "123",
        name: "chapter1", 
        sections: [
          {
           id: "4r4",
           name: "section1",
           items: [
            {
              id: "443",
              name: "some item"
            }
           ]
          }
        ]
      }, 
      {
        id: "222",
        name: "chapter2",
        sections: []
      }
     ]
  }

I have code that adds or inserts a new chapter object that is working. I am using:

// for creating a new chapter:
setSelectedBook(old => {
   return {
     ...old,
     chapters: [
       ...old.chapters, 
       newChapter // insert new object
      ]
   }
})

And for the chapter update, this is working:

setSelectedBook(old => {
    return {
       ...old,
       chapters: [
         ...old.chapters.map(ch => {
           return ch.id === selectedChapterId
             ? {...ch, name: selectedChapter.name}
             : ch
           })
       ]
    }
})

But for my update/create for the sections, I’m having trouble using the same approach. I’m getting syntax errors trying to access the sections from book.chapters. For example, with the add I need:

// for creating a new section:
setSelectedBook(old => {
   return {
     ...old,
     chapters: [
       ...old.chapters,
       ...old.chapters.sections? 
       newSection // how to copy chapters and the sections and insert a new one?
      ]
   }
})

I know with React you’re supposed to return all the previous state except for what you’re changing. Would a reducer make a difference or not really?

I should note, I have 4 simple lists in my ui. A list of books/chapters/sections/items, and on any given operation I’m only adding/updating a particular level/object at a time and sending that object to the backend api on each save. So it’s books for list 1 and selectedBook.chapters for list 2, and selectedChapter.sections for list 3 and selectedSection.items for list 4.

But I need to display the new state when done saving. I thought I could do that with one bookState object and a selectedThing state for whatever you’re working on.

Hopefully that makes sense. I haven’t had to do this before. Thanks for any guidance.

Advertisement

Answer

for adding new Section

setSelectedBook( book =>{
   let selectedChapter = book.chapters.find(ch => ch.id === selectedChapterId )

   selectedChapter.sections=[...selectedChapter.sections, newSection ]

    return {...book}

})

For updating a section’s name

setSelectedBook(book=>{
   let selectedChapter = book.chapters.find(ch => ch.id === selectedChapterId )
   let selectedSection = selectedChapter.sections.find(sec => sec.id === selectedSectionId )

   selectedSection.name = newName

    return {...book}
})

For updating item’s name

setSelectedBook(book =>{
   let selectedChapter = book.chapters.find(ch => ch.id === selectedChapterId )
   let selectedSection = selectedChapter.sections.find(sec => sec.id === selectedSectionId )
   let selectedItem = selectedSection.items.find(itm => itm.id === selectedItemId) 

   selectedItem.name = newItemName

    return {...book}
})

I hope you can see the pattern.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement