简体   繁体   English

React state 中的所有对象都已修改,而单个 object 应更改

[英]All objects in React state are modified while a single object should change

I am working on a form which is used to (later) save a game in which several teams can take part in and such a game is made of multiple rounds .我正在研究一种表格,用于(稍后)保存一个game ,其中几个teams可以参加,这样的game是由多rounds组成的。

My React state thus contains an array of TeamInGame objects:因此,我的React state 包含一组TeamInGame对象:

export default class TeamInGame{

    constructor(nbRounds=2,name=''){

        this.nbRounds = nbRounds;
        this.name = name;
        this.scores = [];

    }

In my form, for each team I can have as many score as the number of rounds in the current game .在我的形式中,对于每支team ,我可以获得与当前gamerounds数一样多的score Thus, in order to be able to track what I should update in my state whenever a field changes (I have only select fields in my app), I pass as props ids such as teamId and roundId .因此,为了能够在字段更改时跟踪我应该在 state 中更新的内容(我的应用程序中只有select字段),我将作为道具 id 传递,例如teamIdroundId

However, as soon as a select value changes, all the identical select fields sees their value change too, as if these values are shared.但是,只要select值发生变化,所有相同的select字段也会看到它们的值发生变化,就好像这些值是共享的一样。

For example, in the code below, if you change the team of a SelectEachTeam , all the SelectEachTeam will be set to this same value (the same apply to SelectScore ).例如,在下面的代码中,如果您更改SelectEachTeam的团队,则所有SelectEachTeam都将设置为相同的值(同样适用于SelectScore )。

I am wondering if I am doing something wrong in handleTeamChange and handleScoreChange ...我想知道我是否在handleTeamChangehandleScoreChange中做错了什么......

Here is the code:这是代码:

FormAddGame.js FormAddGame.js

import React, { Component } from 'react';
import SelectEachTeam from './SelectEachTeam'
import { groups } from '../data.json'
import 'bulma/css/bulma.css'
import  TeamInGame  from '../misc/TeamInGame'

class FormAddGame extends Component {

    constructor(props) {
        super(props)
        this.state = {
            nbRounds: 1,
            nbTeams: 3,
            group: groups[0].name,
            teamsInGroup: this.getTeams(groups[0].name),
            teamsInGame: new Array(3).fill(new TeamInGame(1)),

        }
    }

    // retrieve teams based on 'grp'
    getTeams = (grp) => {
        const groupId = groups.findIndex((el) => (el.name === grp))
        return groups[groupId].teams
    }

    // whenever group changes -> retrieve teams belonging to this 
    // group
    handleGroupChange = e => {
        let group = e.target.value
        const teams = this.getTeams(group)
        // update 'group' and 'teams'
        this.setState({ group, teamsInGroup: teams })

    }

    // whenever a team changes
    handleTeamChange = (e, teamId) => {
        console.log(`handleTeamChange: ${teamId}: ${e.target.value}`)
        let teamsInGame = {...this.state.teamsInGame}
        teamsInGame[teamId].name = e.target.value
        console.log(teamsInGame)
        this.setState({ teamsInGame })
    }

    // whenever a score change
    handleScoreChange = (score, teamId, roundId) => {
        console.log(`handleScoreChange: teamId: ${teamId} / roundId: ${roundId} set to ${score}`)
        let teamsInGame = {...this.state.teamsInGame}
        teamsInGame[teamId].scores[roundId] = score
        this.setState({ teamsInGame: teamsInGame })
    }


    handleSubmit = e => {
        e.preventDefault()
        console.log('in handleSubmit()')

    }



    addTeam = () => {
        let nbTeams = this.state.nbTeams
        nbTeams +=1
        this.setState({nbTeams})
    }

    addRound = () => {
        let nbRounds = this.state.nbRounds
        nbRounds += 1
        this.setState( { nbRounds })
    }

    removeTeam = () => {
        let nbTeams = this.state.nbTeams
        nbTeams -=1
        this.setState({nbTeams})

    }


    render() {

        // group selection
        const selectGroup = Object.keys(groups).map(k => {

            return (<option key={k} value={groups[k].name}> {groups[k].name} </option>)
        }
        )

        // 'nbTeams' SelectEachTeam field (1 team select + 'nbRounds' SelectScore)
        const selectEachTeams = Array.from(Array(this.state.nbTeams).keys())
            .map(k => <SelectEachTeam 
                    handleScoreChange={this.handleScoreChange}
                    handleTeamChange={this.handleTeamChange}
                    deleteBox={this.removeTeam}
                    key={k} 
                    numTeam={k+1} 
                    teams={this.state.teamsInGroup} 
                    nbRounds={this.state.nbRounds}
                    teamsInGame={{...this.state.teamsInGame}}
                    teamId={k} />)



        return (
            <section className="section">
                <div className="container">
                    <form onSubmit={this.handleSubmit}>
                        <div className="box">
                            <div className="field">
                                <label className='label'>Poule</label>
                                <div className="control">
                                    <div className="select is-danger">
                                        <select onChange={this.handleGroupChange} value={this.state.group}>
                                            { selectGroup }
                                        </select>
                                    </div>
                                </div>
                            </div>
                        </div>

                        {selectEachTeams}

                        <div className="field is-grouped">
                            <p className="control">
                                <button type="submit" className="button is-danger is-rounded">Enregistrer</button>
                            </p>
                            <p className="control">
                            <i className="fas fa-plus-circle"/>
                                <button type="button" onClick={this.addTeam} className="button is-info is-rounded">Ajouter une équipe </button>
                            </p>
                            <p className="control">
                                <button type="button" onClick={this.addRound} className="button is-info is-rounded">Ajouter une manche </button>
                            </p>

                        </div>
                    </form>
                </div>

            </section>
        );
    }
}

export default FormAddGame;

SelectEachTeam.js SelectEachTeam.js

 import React from 'react';
    import SelectScore from './SelectScore'


    const SelectEachTeam = ({ deleteBox, numTeam, teams, nbRounds, handleTeamChange, handleScoreChange, teamsInGame, teamId }) => {

        const selectTeams = Object.keys(teams).map((k, v) => {

            return (<option key={k} value={teams[k].name}> {teams[k].name} </option>)

        }

        )

        const selectScores = Array.from(Array(nbRounds).keys())
            .map(k => <SelectScore 
                key={k} scoreMax={50} 
                handleScoreChange={handleScoreChange} 
                teamId={teamId} 
                roundId={k} 
                value={teamsInGame[teamId].scores[k]}   />)

        return (
            <div className="box">
                <div className="container">
                    <div className="field">
                        <label className="label">{`Equipe ${numTeam}`}</label>
                        <div className="control">
                            <div className="select is-success">
                                <select onChange={(e) => handleTeamChange(e, teamId)} value={teamsInGame[teamId].name}>
                                    {selectTeams}
                                </select>
                            </div>
                        </div> 
                        </div>           
                        <nav className="level">
                            <div className="level-left">
                                {selectScores}
                            </div>
                            <div className="level-right">
                                <button type="button"  onClick={deleteBox} className="delete"/>
                            </div>
                        </nav>

                        </div>
                </div>

        )


    }

    export default SelectEachTeam;



**SelectScore.js**

import React from 'react';
import 'bulma/css/bulma.css'

const SelectScore = ({ scoreMax, handleScoreChange, value, teamId, roundId }) => {

    console.log(`${teamId}/${roundId}`)

    scoreMax += 1

    const arr = Array.from(Array(51).keys())
        .map(k => (
            <option key={k} value={k}> {k} </option>
        ))



    return (
        <div className="level-item">
            <div className="field">
                <div className="control">
                    <div className="select">
                        <select  onChange={ (e) => handleScoreChange(e.target.value, teamId, roundId) }  value={ value }>
                            {arr}
                        </select>
                    </div>
                </div>

            </div>
        </div>
    );
}

export default SelectScore;

And the data.json content:data.json内容:

{
    "groups": [
    {
      "name": "Poule A",
      "teams": [{
        "name": "A1"
      },
      {
        "name": "A2"
      },
      {
        "name": "A3"
      },
      {
        "name": "A4"
      },
      {
        "name": "A5"
      }
    ]
    },

    {
      "name": "Poule B",
      "teams": [{
        "name": "B1"
      },
      {
        "name": "B2"
      },
      {
        "name": "B3"
      },
      {
        "name": "B4 "
      },
      {
        "name": "B5"
      },
      {
        "name": "B6"
      }
    ]
    },

    {
      "name": "Poule C",
      "teams": [{
        "name": "C1"
      },
      {
        "name": "C2"
      },
      {
        "name": "C3"
      },
      {
        "name": "C4 "
      }

    ]
    }
  ]
}

The problem came from the fact that I initialized teamsInGame in my state with Array(3).fill(new TeamInGame(1) , a copy of a single TeamInGame object being thus passed at each index.问题来自这样一个事实,即我在teamsInGame中使用Array(3).fill(new TeamInGame(1)初始化了 teamsInGame,因此在每个索引处传递了单个TeamInGame object 的副本。

Replacing this line with Array(3).fill().map(k => new TeamInGame(...)) solves it.Array(3).fill().map(k => new TeamInGame(...))替换这一行可以解决它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM