Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

145
Views
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.

about 4 years ago · Juan Pablo Isaza
3 answers
Answer question

0

I think the map should work for this use case, like in your example.

setSelectedBook(old => {
    return {
       ...old,
       chapters: [
         ...old.chapters.map(ch => {
           return { ...ch, sections: [...ch.sections, newSection] }
           })
       ]
    }
})

In your last code block you are trying to put chapters, sections and the new section into the same array at the same level, not inside each other.

about 4 years ago · Juan Pablo Isaza Report

0

Updating deep nested state objects in React is always difficult. Without knowing all the details of your implementation, it's hard to say how to optimize, but you should think hard about different ways you can store that state in a flatter way. Sometimes it is not possible, and in those cases, there are libraries like Immer that can help that you can look in to.

Using the state object you provided in the question, perhaps you can make all of those arrays into objects with id for keys:

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

With this, you no longer need to use map or find when setting state.

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

// for updating a chapter:
setSelectedBook(old => {
   return {
     ...old,
     chapters: {
       ...old.chapters, 
       [selectedChapter.id]: selectedChapter,
     }
   }
})

// for updating a section:
setSelectedBook(old => {
   return {
     ...old,
     chapters: {
       ...old.chapters, 
       [selectedChapter.id]: {
         ...selectedChapter,
         sections: {
           [selectedSectionId]: selectedSection
         }
       },
     }
   }
})

Please let me know if I misunderstood your problem.

about 4 years ago · Juan Pablo Isaza Report

0

for adding new Section

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

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

    return old

})

For updating a section's name

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

   selectedSection.name = newName

    return old

})

For updating item's name

setSelectedBook(old=>{
   let selectedChapter = old.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 old

})

I hope you can see the pattern.

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!