Reputation: 321
I have an array of 6 objects which have a uid and nothing else. This is so I can repeat over them and have some placeholder content until an object is ready to be added into the array. I set a unique key when a new object is selected. However if I select the same object twice, even though I'm setting a unique key. It seems to update the unique key on the duplicate item (even though the unique key is different).
Might be easier to see the code/app in action here, an example of the problem would be clicking squirtle then blastoise, take a note of the uid's shown. Then click squirtle again and for some reason it updates the old squirtle with the new squirtles uid causing a duplicate key error. https://codesandbox.io/s/l75m9z1xwq or see code below. Math.random is just placeholder until I can get this working correctly.
const initState = {
party: [
{ uid: 0 },
{ uid: 1 },
{ uid: 2 },
{ uid: 3 },
{ uid: 4 },
{ uid: 5 }
]
};
When I click on something this is triggered:
handleClick = pokemon => {
// setup a uid, will need a better method than math.random later
pokemon.uid = Math.random();
this.props.addToParty(pokemon);
};
This then calls a dispatch which triggers the following reducer. Which essentially just checks if the object has no normal ID then replace the content with the payload sent over. It does this but also somehow updates any previous objects with the same uid even though the if statement does not run against them.
const rootReducer = (state = initState, action) => {
if (action.type === "ADD_POKEMON") {
let foundFirstEmptyPoke = false;
const newArray = state.party.map((pokemon, index) => {
if (typeof pokemon.id === "undefined" && foundFirstEmptyPoke === false) {
foundFirstEmptyPoke = true;
pokemon = action.payload; // set the data to the first object that ios empty
}
// if we get to the last pokemon and it's not empty
if (index === 5 && foundFirstEmptyPoke === false) {
pokemon = action.payload; // replace the last pokemon with the new one
}
return pokemon;
});
return {
party: newArray
};
}
return state;
};
Upvotes: 1
Views: 884
Reputation: 1590
The problem here is that, when you click to select a pokemon, you mutate the data you retrieved from the API:
handleClick = pokemon => {
pokemon.uid = Math.random(); // HERE
this.props.addToParty(pokemon);
};
You actually mutate the react state. What you should do is clone your pokemon data object, add an uid to the clone you just generated and update your redux state with it:
handleClick = pokemon => {
this.props.addToParty({
...pokemon,
uid: Math.random()
});
};
That way, no references to the actual react state are kept. Because that was what was happening when you say it updates the old squirtle with the new squirtles uid
. When you tried to add another pokemon, you updated the data you retrieved from your API which was also referenced from your first pokemon slot (from your redux state).
Upvotes: 3
Reputation: 707
You are mutating the state. Use spread syntax ***
to copy the state before updating.
return {
...state,
party: newArray
}
Upvotes: 0
Reputation: 3873
In react/redux it's always better to not mutate objects:
this.props.addToParty({...pokemon, uid: Math.random()});
Upvotes: 1