Tengo un objeto de estado en React que se parece a esto (libro/capítulo/sección/elemento):
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: [] } ] }Tengo un código que agrega o inserta un nuevo objeto de capítulo que funciona. Estoy usando:
// for creating a new chapter: setSelectedBook(old => { return { ...old, chapters: [ ...old.chapters, newChapter // insert new object ] } })Y para la actualización del capítulo, esto está funcionando:
setSelectedBook(old => { return { ...old, chapters: [ ...old.chapters.map(ch => { return ch.id === selectedChapterId ? {...ch, name: selectedChapter.name} : ch }) ] } })Pero para mi actualización/creación de las secciones, tengo problemas para usar el mismo enfoque. Recibo errores de sintaxis al intentar acceder a las secciones de book.chapters. Por ejemplo, con el complemento necesito:
// 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? ] } })Sé que con React se supone que debes devolver todo el estado anterior excepto lo que estás cambiando. ¿Un reductor haría una diferencia o no realmente?
Debo señalar que tengo 4 listas simples en mi interfaz de usuario. Una lista de libros/capítulos/secciones/elementos, y en cualquier operación dada, solo estoy agregando/actualizando un nivel/objeto en particular a la vez y enviando ese objeto a la API de back-end en cada guardado. Así que son libros para la lista 1 y libro.capítulos seleccionados para la lista 2, y capítulos.secciones seleccionados para la lista 3 y sección.elementos seleccionados para la lista 4.
Pero necesito mostrar el nuevo estado cuando termine de guardar. Pensé que podría hacer eso con un objeto bookState y un estado de cosa seleccionada para lo que sea que estés trabajando.
Esperemos que eso tenga sentido. No he tenido que hacer esto antes. Gracias por cualquier orientación.
Creo que el mapa debería funcionar para este caso de uso, como en su ejemplo.
setSelectedBook(old => { return { ...old, chapters: [ ...old.chapters.map(ch => { return { ...ch, sections: [...ch.sections, newSection] } }) ] } })En su último bloque de código, está tratando de colocar capítulos, secciones y la nueva sección en la misma matriz al mismo nivel, no uno dentro del otro.
Actualizar objetos de estado anidado profundo en React siempre es difícil. Sin conocer todos los detalles de su implementación, es difícil decir cómo optimizar, pero debe pensar mucho en las diferentes formas en que puede almacenar ese estado de una manera más plana. A veces no es posible, y en esos casos, hay bibliotecas como Immer que pueden ayudar y que puedes consultar.
Usando el objeto de estado que proporcionó en la pregunta, tal vez pueda convertir todas esas matrices en objetos con id para claves:
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: {}, } ] } Con esto, ya no necesita usar el map o find al configurar el estado.
// 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 } }, } } })Por favor, hágamelo saber si entendí mal su problema.
para agregar una nueva sección
setSelectedBook(old=>{ let selectedChapter = old.chapters.find(ch => ch.id === selectedChapterId ) selectedChapter.sections=[...selectedChapter.sections, newSection ] return old })Para actualizar el nombre de una sección
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 })Para actualizar el nombre del elemento
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 })Espero que puedas ver el patrón.