Adding to an array within an array state in ReactJS - reactjs

I'm trying to add functionality for adding to a state, more specifically "list", in 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}
]
}
],
Right now I have a component that displays a form for the user to enter new values for type revenue gender and price.
Here is the code for the component(not including the forms and text input html):
state = {
}
//when changes occur in text input fields
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault();
this.props.addShoe(this.state);
And in the root component i have the addShoe function:
addShoe = (shoe) => {
shoe.list.id = Math.random();
//returns a new array so no alteration of original array
let shoeList = [...this.state.shoeList, shoe];
this.setState({
shoeList: shoeList
})
}
Trying this code gives me an error saying shoe.list.id is undefined? Also I think I'm missing something to add in the component file specifically in the state. Also is there any way to directly access list like this.state.shoeList.list? I'm not sure if i have to add list to shoeList. Any help would be great thanks

In your example, if the intention is to add an item to specifically the Nike list:
addShoe = (shoe) => {
this.setState({
// return an altered copy of the array via map
shoeList: this.state.shoeList.map(brandItem => {
if (brandItem.name === 'Nike') {
return {
// use spread syntax for other object properties to persist
...brandItem,
list: [
// use spread syntax to keep the original items in the list
...brandItem.list,
{
// assuming shoe is an object without an id property
...shoe,
id: Math.random()
}
]
}
} else {
return brandItem;
}
})
}

Related

React: setState - which is more correct?

I have a react class component that has a state object and I want to update the object with setState. I can get the state to update correctly two different ways and would like to know if one is more correct than the other.
this.state = {
people: {
name: "",
typeofComponent: "class",
}
};
onChange = e => {
// option 1
this.setState((prevState) => ({
people: {
...prevState.people, name: e.target.value
}
}));
// option 2
this.setState({ people: { name: e.target.value }});
}
Your second option would work if the state has only 1 field (name in your case). If you set that way it will overwrite the whole people. That's why we need to use spread operator inorder to make sure the other states are not lost
let a = {
people: {
name: "",
typeofComponent: "class",
}
}
let newName = 'newname'
let withSpread= {people:{...a.people,name:newName}}
let withoutSpread = {people:{name:newName}}
console.log(withSpread)
console.log(withoutSpread)

How to build an array using form values in Angular

I have the function in the component as below:
updateInfo(info: NgForm) {
const changedValues = Object.keys(info.controls)
.filter(key => info.controls[key].dirty === true)
.map(key => {
return { control: key, value: info.controls[key].value }
});
console.log(changedValues);
}
This function emits the changes form values, but now I want to build an array using changedValues as below:
{
firstName: "john",
lastName: "sam"
}
How could I implement it?
Edit: currently I'm getting like this in the console log
[
{firstName: "john"},
{lastName: "sam"}
]
Don't use map because that returns an array. Instead use forEach and add the items to your object:
updateInfo(info: NgForm) {
const changedValues = {};
Object.keys(info.controls).forEach(key => {
if (info.controls[key].dirty) {
changedValues[key] = info.controls[key].value;
}
});
console.log(changedValues);
}

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.

useFieldArray react-hook-form

There is something that I am blocking and it makes no sense at all. For you who are familiar with react-hook-form, I am attempting to create a dynamic field array that renders according to the state object. The thing is, it renders on the first render but not on the second render.
Example:
let subK = [{ name: '' }]
if (kategories[kategori] !== undefined) {
//subK = kategories[kategori].subKategori.map(x => ({ name: JSON.stringify(x) }))
subK = kategories[kategori].subKategori.map(x => ({ name: (x) }))
}
console.log(subK) // it logs[{name: 'kat1'},{name: 'kat2'}]
//defines the form
const { register, control, handleSubmit } = useForm({
defaultValues: {
subKategori: subK
}
});
does not render subK.
But if I do
let subK = [{ name: '' }]
if (kategories[kategori] !== undefined) {
//subK = kategories[kategori].subKategori.map(x => ({ name: JSON.stringify(x) }))
subK = kategories[kategori].subKategori.map(x => ({ name: (x) }))
}
console.log(subK)
//defines the form
const { register, control, handleSubmit } = useForm({
defaultValues: {
subKategori: [{name: 'kat1'},{name: 'kat2'}]
}
});
it renders as it is supposed too.
What am I doing wrong?
There is a minor issue in the structure of subKategori at line number 8. It seems an array is in stringified form. But for map, you need an array. Converting it as following at line number 8 shall work :
....
kat1: {
subKategori: ["kat1", "kat2"]
},
...
Here is the updated sandbox link

Hooks and immutable state

I am apologising in advance if this questions has already been answered before (and also for the long post, but I've tried to be as specific as I could be). But, the answers I found does not completely satisfy me.
I want to use the new amazing React Hooks for my project. And for what I've been doing so far, it has been straight forward.
Now I have ran into a more complex example, and I feel unsure on how I best should tackle this one.
Let's say, I have more of a complex object (at least it's not flat) in my state.
{
persons: [
{
id: 1,
name: 'Joe Doe',
age: 35,
country: 'Spain',
interests: [
{ id: 1, en: 'Football', es: 'FĂștbol' },
{ id: 2, en: 'Travelling', es: 'Viajar' }
]
},
{
id: 2,
name: 'Foo Bar',
age: 28,
country: 'Spain',
interests: [
{ id: 3, en: 'Computers', es: 'Computadoras' },
{ id: 4, en: 'Cooking', es: 'Cocinar' }
]
}
],
amount: 2,
foo: 'bar'
}
What is the best way to:
Add an item (an object) to my colleagues array
Add an item to a specific "interests" array?
Manipulate the value of a property within an object in the array?
Change a value outside the persons array, for example foo?
using the useState hook?
The following code examples will try to illustrate each question. They're not tested...
Let us consider I have a container that begins with this.
It also includes the functions that are split up in the rest of the post.
const [colleagues, setColleagues] = useState({});
// using the useEffect as "componentDidMount
useEffect(() => {
// receiving data from ajax request
// and set the state as the example object provided earlier
setColleagues(response.data);
}, []);
1) So for the first question. Is this valid?
Or do I need to to make sure that each and every object in my persons array is destructured?
const onAddNewColleague = () => {
// new colleague, this data would be dynamic
// but for the sake of this example I'll just hard code it
const newColleague = {
name: 'Foo Baz Bar',
age: 30,
country: 'Spain',
interests: [
{ en: 'Cats', es: 'Gatos' }
]
};
// creates a copy of the state
// targets the "persons" prop and adds a new array
// where the new colleague is appended
const newState = {
...colleagues,
persons: colleagues.concat(newColleague)
};
// updates the state
setColleagues(newState);
};
2) This feels wrong as I end up updating the entire persons array instead of just the interest array for a specific person.
const onAddNewInterest = (personId) => {
// a new interest
const newInterest = {
en: 'Languages', es: 'Idiomas'
};
// find the person according to personId
// and update the interests
const updatedPersons = colleagues.persons.map(person => {
if(person.id === personId) {
return {
...person,
interests: person.interests.concat(newInterest);
};
}
return person;
});
// create a copy of the state
const newState = {
...colleagues,
persons: [...updatedPersons]
};
setColleagues(newState);
};
3) As the second example, this one feels wrong too as I am updated the entire persons array when in fact I might just want to change the age of one specific person
const onChangeValue = (personId, key, value) => {
// find the person
const updatedPersons = colleagues.persons.map(person => {
if(person.id === personId) {
// key, could be age?
return {
...person,
[key]: value
};
}
return person;
});
// create a copy of the state
const newState = {
...colleagues,
persons: [...updatedPersons]
};
setColleagues(newState);
};
4) Is this valid, or do I need to destruct every part of my colleagues object separately?
const onChangeOtherValue = (key, value) => {
// for example changing the value foo
const newState = {
...colleagues,
[key]: value
};
setColleagues(newState);
};
I do have a feeling that only the concept of the first function is valid, while the rest of them are not.
Can this be done easily, or should I just use an immutable-helper?
Thanks in advance!
Updated examples to get syntax, right. Thanks Valerii.
To clarify
What I'm really after here is best practise to handle use cases like this one. I want to make sure my state is updated in the most correct and efficient way. So feel free to rip my examples a part or write new ones - I'm all ears. It is not necessary to simply modify mine to fit this post (unless they actually turn out to be good).
1) OK
2)
const updatedPersons = colleagues.persons.map(person => {
if(person.id === personId) {
return {
...person,
interests: person.interests.concat({ en: 'Test', es: 'Test' })
};
}
return person;
});
const newState = {
...colleagues,
persons: updatedPersons
};
3)
const updatedPersons = colleagues.persons.map(person => {
if(person.id === personId) {
return {
...person,
[key]: value
};
}
return person;
});
// create a copy of the state
const newState = {
...colleagues,
persons: updatedPersons
};
4) OK
for the first one, i would do this way,
const newState = {
...colleagues,
persons: [...persons, newColleague]
};

Resources