【问题标题】:setState nested object in a map function地图函数中的 setState 嵌套对象
【发布时间】:2021-12-02 15:33:19
【问题描述】:

我的状态对象是一个包含对象的数组。PS:(随意更改结构)这是结构:

    {
        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() }] }, ],
            }
        ]
    }

我想更改每个通道对象中的所有 isChecked,目前我正在使用此功能,但它没有按预期工作。

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)

};

【问题讨论】:

    标签: reactjs setstate


    【解决方案1】:

    可能是学习如何使用“immer”库的好时机。它非常适合需要对嵌套太深的对象进行更改的情况。没有它,对您正在处理的对象进行更改会变得非常混乱且难以处理。

    Immer 真的很容易在一两天内上手和学习。如果您使用它,您的代码可以简化为:

    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;
                        })
                    })
                })
            })
        })
    }
    

    我没有运行此代码,因此不能 100% 确定它是否有效。无论哪种方式,像这样的带有 immer 的东西都会起作用并且更容易处理。请注意,您不必处理扩展语法或任何其他语法,而 immer 实际上会创建一个新对象,因此它避免了与可变数据相关的任何麻烦。

    【讨论】:

      【解决方案2】:

      应该这样做:

      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));
      }
      

      这样使用:

      setList(list => toggleChecked(list));
      

      这是另一个帮助从数组中获取随机项的函数(我注意到您在代码中重复了很多数学表达式来执行此操作):

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

      这样使用:

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

      【讨论】:

      • 感谢您推荐帮助函数。主要功能仍然给我与我最初相同的结果
      • @Ibra 您在问题中说:“我想更改每个通道对象中的所有 isChecked ......但它没有按预期工作”。在您的示例(和我的示例)中,使用 channel.Name === channelName 将意味着只有名称匹配的通道才会切换其文档的检查值。那是“意图”吗?如果没有,你能澄清一下你真正打算发生什么吗?
      【解决方案3】:

      检查一下:

      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;
          })
      };
      

      【讨论】:

      • 我收到了这个错误 Uncaught TypeError: ConnectionsArray.find is not a function
      • 对不起。我编辑了我的答案
      • Uncaught TypeError: Cannot read properties of undefined (reading 'map')
      猜你喜欢
      • 2017-10-22
      • 2020-02-19
      • 1970-01-01
      • 2020-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-12
      • 1970-01-01
      相关资源
      最近更新 更多