反应:一次改变嵌套状态

时间:2020-08-09 04:57:33

标签: reactjs typescript

因此,在典型的编程方案中,如果您对一个对象进行突变,则它会在任何地方对对象进行突变。但是,在React中,由于状态是不可变的并且只能通过每个组的集合*进行更改,因此,如果状态彼此嵌套(如下面所示的情况),则只有currentMember的名称会更改,而currentTeam或team中currentMember的版本不会更改。我必须逐一检查并逐个进行变异,这很麻烦。

一次改变多个状态或达到类似效果的最佳方法是什么?我已经通过建立索引来做到这一点,但是使用起来比较麻烦,而且我不知道是否有教科书挂钩可以解决此问题。

import React, { useState } from 'react'

interface Member {
    name: string
}

interface Team {
    name: string
    members: Member[]
}

export default (props: {}) => {
    const [teams, setTeams] = useState<Team[]>([
        {
            name: 'Team One',
            members: [{ name: 'Wyatt' }, { name: 'Michael' }]
        }
    ])
    const [currentTeam, setCurrentTeam] = useState<Team>(teams[0])
    const [currentMember, setCurrentMember] = useState<Member>(currentTeam.members[0])

    return (
        <>
            <h1>${currentMember.name}</h1>
            <button onClick={() => setCurrentMember(currentMember => { ...currentMember, name: 'Zach' })}>
                Change current member name to Zach!
            </button>
        </>
    )
}

1 个答案:

答案 0 :(得分:1)

如上所述,通过这种方式使用状态会使事情变得有些复杂。您应该有一个包含所有团队的基本状态,然后按索引引用对您来说很重要的位。

例如,您的teams状态很好。您的currentTeamcurrentMember状态应该是要映射到teams中的状态的索引或其他引用。

因此,按照特定的术语,我将在此处更改代码的格式(请原谅我,因为我没有编写TypeScript,所以我将使用纯正的Javascript以避免产生拼写错误):

import React, { useState } from 'react'

// interface Member {
//    name: string
//}

// interface Team {
//    name: string
//    members: Member[]
//}

export default (props: {}) => {
    const [teams, setTeams] = useState([
        {
            name: 'Team One',
            members: [{ name: 'Wyatt' }, { name: 'Michael' }]
        }
    ])
    const [currentTeamIndex, setCurrentTeam] = useState(0)
    const [currentMemberIndex, setCurrentMember] = useState(0)

    return (
        <>
            <h1>${teams[currentTeamIndex].members[currentMemberIndex]}</h1>
            <button onClick={() => setTeams(teams => ({
                // Shallow copy the teams via mapping through them
                ...teams.map((team, teamIndex) => {
                  // If the current team index isn't the index we're on right now, then just
                  // return the existing team in its given place.
                  if (teamIndex !== currentTeamIndex) return team
                  
                  // If we're at this point, it means the teamIndex matches the currentTeamIndex
                  // and we need to mutate this. We'll just do that by returning a new object
                  return {
                    ...team, // Make sure we don't miss anything
                    ...members.map((member, memberIndex) => {
                      // Same as the outer map, if the current member index isn't the same as the
                      // given memberIndex, then just return the member we're on, we're not mutating it
                      if (memberIndex !== currentMemberIndex) return member
                      
                      return {
                        ...member,
                        name: 'Zach'
                      }
                    })
                  }
                }
              })
            >
                Change current member name to Zach!
            </button>
        </>
    )
}

如您所见,将对象深入到对象并不是那么简单,但是可以在不更改原始数据的情况下进行操作-您可以通过使用Array函数动态重建数据来完成所需的操作,传播语法和索引引用。

您可能还想考虑将其限制在化简器中,并通过包含团队ID索引和成员ID索引的特定操作来使它更具可组合性。