[英]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
,我可以获得与当前game
的rounds
数一样多的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 传递,例如teamId
和roundId
。
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
...我想知道我是否在
handleTeamChange
和handleScoreChange
中做错了什么......
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.