Ibra
Ibra

Reputation: 1072

setState nested object in a map function

My State Object is a array that holds objects.PS:(Feel free to change the structure) Here is the structure:

    {
        type: ListOfTypes[Math.floor(Math.random() * ListOfTypes.length)],
        name: ListOfNames[Math.floor(Math.random() * ListOfNames.length)],
        id:  nanoid(),
        channels: [
            {
                id:  nanoid(),
                Name: ListOfChannels[Math.floor(Math.random() * ListOfChannels.length)],
                Files: [ { folder: "Folder",  documents: [ { doc: "WordDoc1.doc", isChecked: false, id:nanoid() }, { doc: "WordDoc2.doc", isChecked: false, id:nanoid() }, { doc: "WordDoc3.doc", isChecked: false, id:nanoid() }, { doc: "WordDoc4.doc", isChecked: false, id:nanoid() }] }, ],
            },
            {
                id:  nanoid(),
                Name: ListOfChannels[Math.floor(Math.random() * ListOfChannels.length)],
                Files: [{ folder: "Folder",  documents: [ { doc: "WordDoc1.doc", isChecked: false, id:nanoid() }, { doc: "WordDoc2.doc", isChecked: false, id:nanoid() }, { doc: "WordDoc3.doc", isChecked: false, id:nanoid() }, { doc: "WordDoc4.doc", isChecked: false, id:nanoid() }] }, ],
            },
            {
                id:  nanoid(),
                Name: ListOfChannels[Math.floor(Math.random() * ListOfChannels.length)],
                Files: [{ folder: "Folder", documents: [ { doc: "WordDoc1.doc", isChecked: false, id:nanoid() }, { doc: "WordDoc2.doc", isChecked: false, id:nanoid() }, { doc: "WordDoc3.doc", isChecked: false, id:nanoid() }, { doc: "WordDoc4.doc", isChecked: false, id:nanoid() }] }, ],
            }
        ]
    }

I want to change all the isChecked in every channel object, currently I'm using this function but it doesn't work as intended.

const handleChange = () => {

    const ConnectionsArray = List.map( (connection) => connection.id == connectionId ?
        {
            ...connection,
            channels: connection.channels.map( (channel) =>  channel.Name == channelName ? {
                ...channel,
                Files: channel.Files.map((file) => ({
                    ...file,
                    documents: file.documents.map((doc) => ({ ...doc, isChecked: !doc.isChecked }) )
                }))

            } : channel)
        } : connection)

    setList(ConnectionsArray)

};

Upvotes: 0

Views: 325

Answers (3)

davood Sandoghsaz
davood Sandoghsaz

Reputation: 684

Check this:

const handleChange = () => {
    setList(prevState => {
        let ConnectionsArray = [...prevState];
        const itemIndex = ConnectionsArray.find(item => item.id === connectionId);
        const connection = {...ConnectionsArray[itemIndex]};
        ConnectionsArray[itemIndex] =  {
            ...connection,
            channels: connection.channels.map( (channel) =>  channel.Name == channelName ? {
                ...channel,
                Files: channel.Files.map((file) => ({
                    ...file,
                    documents: file.documents.map((doc) => ({ ...doc, isChecked: !doc.isChecked }) )
                }))

            } : channel)
        };
        return ConnectionsArray;
    })
};

Upvotes: 0

jsejcksn
jsejcksn

Reputation: 33796

This should do it:

function toggleChecked (connections) {
  return connections.map(connection => (connection.id === connectionId
    ? {
      ...connection,
      channels: connection.channels.map(channel => (channel.Name === channelName
        ? {
          ...channel,
          Files: channel.Files.map(file => ({
            ...file,
            documents: file.documents.map(doc => ({
              ...doc,
              isChecked: !doc.isChecked,
            })),
          })),
        }
        : channel)),
    }
    : connection));
}

Use like this:

setList(list => toggleChecked(list));

Here's another function to help with getting a random item from an array (I notice you're repeating a lot math expressions to do this in your code):

function getRandomElement (array) {
  return array[Math.floor(Math.random() * array.length)];
}

Use like this:

// before
ListOfTypes[Math.floor(Math.random() * ListOfTypes.length)]

// after
getRandomElement(ListOfTypes)

Upvotes: 2

ICW
ICW

Reputation: 5779

Probably a good time to learn how to use the "immer" library. It's perfect for situations like these where you need to make changes too deeply nested objects. Without it, making changes to objects like the one you're dealing with gets really messy and hard to deal with.

Immer is really easy to pick up and learn in a day or two. If you used it, your code could be reduced to this:

import produce from 'immer';

const handleChange = ()=>{
    const ConnectionsArray = produce(List, draft=>{
        draft.forEach((object)=>{
            object.channels.forEach((channel)=>{
                channel.Files.forEach((file)=>{
                    file.documents.forEach((document)=>{
                        document.isChecked = !document.isChecked;
                    })
                })
            })
        })
    })
}

I didn't run this code so not 100% sure it works. Either way, something like this with immer will work and be a lot easier to deal with. Notice you don't have to deal with the spread syntax or any of that, and immer will actually be creating a new object so it avoids any of the headaches associated with mutable data.

Upvotes: 1

Related Questions