I am building a user flow to allow users to enter stats for a video game that they played. My react component renders an excel like table where they can manually type in the stats (like an excel spreadsheet where the y-axis is a column of players and the x-axis are the stats for each player.
When I update a particular stat for a particular player, every player's stat value for that particular stat is also changed. For example, if I update Player A with 5 goals every other player on the team will also have 5 goals. This shouldn't happen.
First, I initialize a matchStats object:
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))
}
Then, I update the matchStats object as it is changed by the user:
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]
})
}
Here is the react code block:
<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>
I would expect that when I change an input value for one stat for a given player, it only changes the stat value for that particular player. However, it changes it for every player.
I've been messing with this for a while now and struggling to see what I'm doing wrong. I thought it might be related to how inputs are rendered in a .map function but I tried separating a test input and the result was the same. Any help is appreciated!
new Array(matchInfo.playersAllowedInGame).fill({is likely the trouble..filljust does one object for the whole shebang. Change it to something likefill().map(e => {stats: statsWithKeys})...and let me know if it works. Likely, you'll only ever want to pass primitives into.fill().fill().map(e => ({stats: statsWithKeys}));(note the extra parens). So did your change work? The problem is thatstatsWithKeysis still just one object as well, so that's a problem. You're going to have to initialize that object inside themapas well. Basically, any time there is just one object being created, it'll be aliased, so there are multiple aliasing problems to resolve.