React Duplicate Key Error - reactjs

I'm getting the following error, I understand what its telling me but I can't figure out how to solve the issue.
flattenChildren(...): Encountered two children with the same key...
I have 2 lists on my page which contain emails. The initial state of my app contains the following data:
const initialState = {
emails: [
{
id: 1, from: 'test.1#test.co.uk', body: 'test1 body', title: 'test 1 title',
},
{
id: 2, from: 'test.2#test.co.uk', body: 'test2 body', title: 'test 2 title',
},
],
draggedEmails: [],
};
The UI of my app lets you drag and drop items from the first list (emails) to the second list (draggedEmails).
In my Redux reducer I have the following code to move emails between the lists.
let newState = {};
//Check if the email exists in my 'emails' array
const doesExistInEmails = state.emails.find(x => x.id === action.id) !== null;
//If it exists then move it to the 'draggedEmails' list
if (doesExistInEmails) {
const filteredEmails = state.emails.filter(e => e.id !== action.emailItem.id);
newState = Object.assign(
{},
state,
{ draggedEmails: [...state.draggedEmails, action.emailItem], emails: filteredEmails }
);
} else {
const filteredEmails = state.emails.filter(e => e.id !== action.emailItem.id);
newState = Object.assign(
{},
state,
{ draggedEmails: [...state.emails, action.emailItem], emails: filteredEmails });
}
return newState;
The problem occurs when I move the items BACK to the emails list, once they have been moved to the 'draggedEmails' list.
The following code is what is used to create the elements and the keys.
createEmailItem(em) {
return React.createElement(
EmailItem, { email: em, key: `${em.id}` });
}
Any help is appreciated,
Thanks.
EDIT: Console.Logged state after moving one item from the 'emails' list to the 'draggedEmails' list. Everything looks as it should.
Object {emails: Array[1], draggedEmails: Array[1]}
EDIT2: Adding render method.
render() {
return (
<div className="email-container" onDrop={this.onDrop} onDragOver={this.allowDrop}>
{this.props.emails.map(this.createEmailItem)}
</div>
);
}

I found the problem. There were 4.
The first is that the following was returning 'undefined' rather than 'null'
const doesExistInEmails = state.emails.find(x => x.id === action.id) !== null;
The second is that my action doesn't have an id, my action has an emailItem which has an id
const doesExistInEmails = state.emails.find(x => x.id === action.emailItem.id) !== undefined;
The third is that I was filtering my emails rather than dragged emails in the following line.
const filteredEmails = state.filter(e => e.id !== action.emailItem.id);
And finally I was assigning the wrong values back when setting the state.
{ draggedEmails: [...state.emails, action.emailItem], emails: filteredEmails });
Should be...
{ emails: [...state.emails, action.emailItem], draggedEmails: filteredEmails });
So overall, I had lots wrong...
Thanks to the guys who commented.

Related

Update nested React state?

I'm trying to update part of a state object that is nested. This is the object:
const [buttonObject, setButtonObject] = useState({
intro: [
{
id: '123',
name: 'first_intro_name',
selected: false,
},
{
id: '124',
name: 'second_intro_name',
selected: false,
},
],
experience: [
{
id: '789',
name: 'first_experience_name',
selected: false,
},
{
id: '8910',
name: 'second_experience_name',
selected: false,
},
],
});
When a button is clicked I want to toggle the selected state. I'm using a click handler that looks like this:
const handleButtonClick = ({ id, selected }) => {
if (id === '123') {
buttonsObject.intro.map(
pref => (pref.selected = pref.id === id ? !pref.selected : selected)
);
setButtonsObject(buttonsObject);
} else if (id === '124') {
buttonsObject.intro.map(
pref => (pref.selected = pref.id === id ? !pref.selected : selected)
);
setButtonsObject(buttonsObject);
}
};
It would handle experiences as well. The issue is that right now it seems like rather than updating the object it just overwrites the object or creates a new one. It also doesnt pass that information back down to the component even though I have it routed correctly so it should.
Is there better/correct syntax for updating nested state like this?
Thanks.
instead of checking again with if condition use higher order array function and spread operator to get optimal solution.
setButtonObject(previous => {
return {
...previous,
info: previous.info.map(item => item.id === id ? {
...item,
selected: true
} ? item)
}
})

Finding an array item in state

I have the following array in my Sate:
this.state = {
currentAsset: null,
assets: [
{ id: uuidv4(), selected: false, text: 'Sorte',},
{ id: uuidv4(), selected: false, text: 'optical_dillusion2322.jpg'},
{ id: uuidv4(), selected: false, text: 'man_read_abook3242332.jpg'},
]
}
I want to find one of these assets and assign it to currentAsset. I have the following function:
handleAssetIDChange(assetID) {
console.log(assetID)
var currentAsset = this.state['assets'][assetID]
console.log(currentAsset)
// this.setState({
// currentAsset: this.state['assets'][assetID],
// openAssetSideBar: true
// }, () => {
// console.log(this.state['assets'][assetID])
// })
}
You can see the commented out part is now working. I am trying to set the currentAsset and then trigger the open of the sidebar to display the contents, but currentAsset is not getting set.
I have the assetID, how can I locate the one I need? Another question i have is in many stackoverflow posts they reference state vars like objects, aka:
this.state.assets but I always get an error when trying to do that.
How can I assign currentAsset and then trigger the openAssetSidebar when it has been set?
I would use a filter use get the object, this should update your currentAsset in the state.
handleAssetIDChange(assetID) {
console.log(assetID)
var currentAsset = this.state['assets'].filter( item => item.id === assetID)[0]
console.log(currentAsset)
this.setState(( prev ) => {
...prev,
currentAsset,
openAssetSideBar: true
})
}
In order to find the asset that you are looking for with a specific id, instead of
var currentAsset = this.state['assets'][assetID]
You should do:
const foundAsset = this.state['assets'].find((el) => el.id === assetID)
then to update the state, you would do:
this.setState((prev) => ({...prev, currentAsset: foundAsset}))

Trying to delete a property in an array that is also within in array in React

I'm trying to add functionality to my application for deleting values in the list array of the following state
shoeList : [
{name: 'Nike',
list : [
{type: 'boots', revenue: '1000000', gender: 'mens', price: '49.99', id: 3},
{type: 'shoes', revenue: '13280100', gender: 'womens', price: '99.99', id: 2}
]
}
],
I understand that ids have to be equal in order for a deletion to occur. Right now i'm trying to access the appropriate row to be deleted(type, revenue, gender, and price) but am not sure how to access properties in an array thats within an array.
Here is the code i have so far
deleteShoe = (id) =>
{
let shoeList = this.state.shoeList.filter(shoe =>{
return (
shoe.list.filter(shoeid =>{
return shoeid.id !== id
}
)
)
});
this.setState({
shoeList: shoeList
})
}
Obviously this code doesn't and can't understand why. The only reason I can think of is that filter can't be nested but if so how would I go about implementing this. Any help would be great
You're close. You're just missing reassignment of shoe.list with updated list of shoes after filtering.
deleteShoe = (id) => {
const { shoeList } = this.state;
const newShoeList = shoeList.map(shoe => {
shoe.list = shoe.list.filter(shoeid => shoeid.id !== id)
return shoe;
});
this.setState({
shoeList: newShoeList
})
}
This should remove appropriate shoe id and reassign your shoe.list with newly filtered shoes list.

How to update a state variable in an array in React?

I have this state and I want to update the properties name and location in a dynamic way.
this.state = {
players: [{
name: '',
location: ''
},
{
name: '',
location: ''
}]
}
The idea is that you can click a button to add another/remove a player. Per player, these input fields should appear (which I achieved), but I'm unable to update the state on change.
Preferably something like this (but I'm unable to make it work for this particular case). Unless there's a better way to achieve this of course (I'm rather new in React).
this.handleChange = (event) => { let obj = {...this.state.obj }; obj [event.target.name] = event.target.value; this.setState({obj }); }
Any help will be appreciated!
you can add temporary id value to the player's list and on change pass the target id and the field changes, and change the state accordingly
const onPlayerChange = ({target : { id , name , vlaue}}) =>{
const newPlayersState = this.state.players.map(player=>{
if(player.id === id) return {...player,[name]:value}
return player;
})
this.setState({players:newPlayersState})
}
addPlayer(name, location) {
const players = {...this.state.players};
players.push({
name,
location
});
this.setState({
players
});
}
removePlayer(name) {
const players = {...this.state.players};
this.setState({
players: players.filter(p => p.name != name)
});
}

SetState of an array of Objects in React

Ok, so I'm so frustrated finding the right solution so I'm posting the problem here. Giving an answer would help me a lot, coz I'm stuck!
the state tree looks like this
this.state = {
itemList : [{
_id : 1234,
description : 'This the description',
amount : 100
}, {
_id : 1234,
description : 'This the description',
amount : 100
}],
}
The problems are :
can not update any specific key in the Object of the array according
to the _id
The previous state should remain intact
answered March 25 2018
This is how you would use setState and prevstate to update a certain attribute of an object in your data structure.
this.setState(prevState => ({
itemList: prevState.itemList.map(
obj => (obj._id === 1234 ? Object.assign(obj, { description: "New Description" }) : obj)
)
}));
answered Dec 12 2019 (REACT HOOKS)
import React, { useState } from 'react';
const App = () => {
const [data, setData] = useState([
{
username: '141451',
password: 'password',
favoriteFood: 'pizza',
},
{
username: '15151',
password: '91jf7jn38f8jn3',
favoriteFood: 'beans'
}
]);
return (
<div>
{data.map(user => {
return (
<div onClick={() => {
setData([...data].map(object => {
if(object.username === user.username) {
return {
...object,
favoriteFood: 'Potatos',
someNewRandomAttribute: 'X'
}
}
else return object;
}))
}}>
{JSON.stringify(user) + '\n'}
</div>
)
})}
</div>
)
}
to update state constructed like this you will have to find index of element you want to update, copy the array and change found index.
it's easier and more readable if you keep list of records as object, with id as a key and record as a value.
The only way to do this will be to copy itemList, modify it, and set the state to it.
update() {
let itemList = this.state.itemList.slice();
//update it
this.setState({ itemList });
}
Best way to update data into an array of objects
onChange={ (e) => this.setState({formData: { ...this.state.formData, 'plan_id': e.target.value}})}

Resources