Javascript 为什么输入的onChange事件会更改对象数组中的多个状态值?

Javascript 为什么输入的onChange事件会更改对象数组中的多个状态值?,javascript,arrays,reactjs,object,Javascript,Arrays,Reactjs,Object,我正在建立一个用户流,允许用户为他们玩的视频游戏输入统计数据。我的react组件呈现一个类似excel的表格,他们可以像excel电子表格一样手动输入统计数据,其中y轴是一列玩家,x轴是每个玩家的统计数据 当我为特定玩家更新特定属性时,每个玩家该属性的属性值也会改变。例如,如果我用5个进球更新球员A,团队中的每个其他球员也会有5个进球。这不应该发生 首先,我初始化matchStats对象: initializeMatchStats = (numGames) => { const {

我正在建立一个用户流,允许用户为他们玩的视频游戏输入统计数据。我的react组件呈现一个类似excel的表格,他们可以像excel电子表格一样手动输入统计数据,其中y轴是一列玩家,x轴是每个玩家的统计数据

当我为特定玩家更新特定属性时,每个玩家该属性的属性值也会改变。例如,如果我用5个进球更新球员A,团队中的每个其他球员也会有5个进球。这不应该发生

首先,我初始化matchStats对象:

initializeMatchStats = (numGames) => {
    const {matchInfo} = this.props;
    const gameArray = new Array(numGames).fill('');

    const matchStats = gameArray.map((game, i) => {
        let statsWithKeys = {};
        let gameInfo = {};

        matchInfo.circuitStats.map(key => {
            statsWithKeys[key.toLowerCase()] = 0
        })

            const players = new Array(matchInfo.playersAllowedInGame).fill({
                stats: statsWithKeys
            }).map(p => {
                return {
                    ...p,
                    dropdownOpen: false,
                    id: Math.random().toString(36)
                }
            })

            gameInfo = {
                players,
                index: i + 1
            }

        return gameInfo
    })
    this.setState({matchStats, numGames, numGamesModalOpen: false, uploadManually: true}, () => console.log('matchStats: ', matchStats))
}
然后,在用户更改matchStats对象时更新它:

updateStatsManually = (val, player, game, header, e) => {

        e.preventDefault();

        const {matchStats} = this.state;
        // matchStats is an array of games in each match. A game is made up of the players (and their respective stats) in the game.
        const { matchInfo } = this.props;

        const gameToUpdate = matchStats.find(g => g.index === game.index)

        let playerToUpdate = gameToUpdate.players.find(p => p.id === player.id)

        let newStats = playerToUpdate.stats;
        newStats[header] = val;
        playerToUpdate.stats = newStats;
        gameToUpdate.players = [...gameToUpdate.players.filter(p => p.id !== player.id), playerToUpdate]

        this.setState({
            matchStats: [...matchStats.filter(g => g.index !== game.index), gameToUpdate]
        })
    }
以下是react代码块:

<Section>
    {matchStats.find(g => g.index === game).players.map((player, i) => <Row key={i}>
        {!uploadManually && <Column>
            <Input disabled style={{textAlign: 'center'}} placeholder={player.name} />
        </Column>}
        {circuitStats.map((header, i) => <Column key={`${i}-${player.id}`}>
            <Input id={`${i}-${player.id}-${header}`} value={player.stats[header.toLowerCase()]} disabled={!uploadManually} onChange={(e) => updateStatsManually(e.target.value, player, currentGame, header.toLowerCase())} />
        </Column>)}
    </Row>)}
</Section>
我希望当我为一个给定的玩家更改一个属性的输入值时,它只会更改该特定玩家的属性值。然而,它改变了每一个球员


我已经在这件事上纠结了一段时间,努力想知道我做错了什么。我认为这可能与.map函数中输入的呈现方式有关,但我尝试分离测试输入,结果是一样的。感谢您的帮助

第一个问题是以下几行:

const players = new Array(matchInfo.playersAllowedInGame).fill({
    stats: statsWithKeys
}) // ... etc ...
通过将一个对象传递到中,每个元素都会填充对该单个对象的引用。堆栈片段在以下最小reprex中清楚地显示了这一点:

const arr=Array4.fill{age:11}; console.logJSON.stringifyarr; arr[0],年龄42岁; console.logJSON.stringifyarr;
console.logarr 第一个问题是以下几行:

const players = new Array(matchInfo.playersAllowedInGame).fill({
    stats: statsWithKeys
}) // ... etc ...
通过将一个对象传递到中,每个元素都会填充对该单个对象的引用。堆栈片段在以下最小reprex中清楚地显示了这一点:

const arr=Array4.fill{age:11}; console.logJSON.stringifyarr; arr[0],年龄42岁; console.logJSON.stringifyarr;
console.logarr 假设你最多有5名球员。在这种情况下,这:

         const players = new Array(matchInfo.playersAllowedInGame).fill().map(p => {
                return {
                   stats: statsWithKeys,
                   dropdownOpen: false,
                   id: Math.random().toString(36)
                }
            })
不是像您预期的那样创建5个“statsWithKeys”,而是对同一个“statsWithKeys”的5个引用

解决此问题的最佳方法是直接在对象本身上使用扩展操作符:

         const players = new Array(matchInfo.playersAllowedInGame).fill().map(p => {
                return {
                   stats: { ...statsWithKeys },
                   dropdownOpen: false,
                   id: Math.random().toString(36)
                }
            });

假设你最多有5名球员。在这种情况下,这:

         const players = new Array(matchInfo.playersAllowedInGame).fill().map(p => {
                return {
                   stats: statsWithKeys,
                   dropdownOpen: false,
                   id: Math.random().toString(36)
                }
            })
不是像您预期的那样创建5个“statsWithKeys”,而是对同一个“statsWithKeys”的5个引用

解决此问题的最佳方法是直接在对象本身上使用扩展操作符:

         const players = new Array(matchInfo.playersAllowedInGame).fill().map(p => {
                return {
                   stats: { ...statsWithKeys },
                   dropdownOpen: false,
                   id: Math.random().toString(36)
                }
            });

你能说明这些结构是在哪里创建的吗?一定有一些别名。@ggorlen我在初始化matchStats对象的地方添加了代码块。希望这有帮助!这很有帮助,谢谢。新的ArraymatchInfo.playersAllowedInGame.fill{可能是问题所在。.fill只对整个shebang执行一个对象。将其更改为类似fill.mape=>{stats:statsWithKeys}…并让我知道它是否有效。很可能,您将只希望将原语传递到.fill.中。不幸的是,这似乎不起作用。我将其更改为:`const players=new ArraymatchInfo.players allowedingame.fill.mapp=>{return{stats:statsWithKeys,dropdownOpen:false,id:Math.random.toString36}}`应该是fill.mape=>{stats:statsWithKeys};注意额外的参数。您的更改也起到了作用吗?问题是statsWithKeys仍然只是一个对象,所以这是一个问题。您还必须在地图中初始化该对象。基本上,任何时候只要创建一个对象,它都会被别名,因此有多个别名问题需要解决。您可以吗这些结构是在什么地方创建的?肯定有一些别名。@ggorlen我在初始化matchStats对象的地方添加了代码块。希望这会有帮助!非常有帮助,谢谢。新的ArraymatchInfo.playersAllowedInGame.fill{可能是问题所在。.fill只对整个shebang执行一个对象。将其更改为类似fill.mape=>{stats:statsWithKeys}…并让我知道它是否有效。很可能,您将只希望将原语传递到.fill.中。不幸的是,这似乎不起作用。我将其更改为:`const players=new ArraymatchInfo.players allowedingame.fill.mapp=>{return{stats:statsWithKeys,dropdownOpen:false,id:Math.random.toString36}}`应该是fill.mape=>{stats:statsWithKeys};注意额外的参数。您的更改也起到了作用吗?问题是statsWithKeys仍然只是一个对象,所以这是一个问题。您还必须在地图中初始化该对象。基本上,任何时候只要创建一个对象,它都会被别名,因此需要解决多个别名问题。