How can I update a nested state with arrays in React? - reactjs

I have a nested array, and I am trying to update some properties, but i don't know the syntax in react to do that.
this.state = {
databasesList: {
name: 'Databases',
toggled: true,
children: [
{
name: 'OneDatabase',
children: [
{ name: 'collection1' },
{ name: 'collection2' }
]
}
]
}
}
I am trying to update with this, but it does not work:
this.setState({ this.state.databasesList.children[0].children: newData })

To set nested state in React you should use JS spread operator so in your example it should be something like this:
this.setState((prevState) => ({
...prevState,
databasesList: {
...prevState.databasesList,
children: {
...prevState.databasesList.children[0],
children: {
newData,
},
},
},
}));

If you want to use lodash set here's a pretty nifty solution.
https://twitter.com/stwilz/status/1092958989208317952
But if you just want to use set on it's own this would be the way to go.
const yourFunction = newData => this.setState(state => set(state, ['databasesList', 'children', 0, 'children'], newData));
The spread operator works fine as well. It just gets super verbose when you have a huge state object.

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)
}
})

update nested state object with array inside

I am having trouble updating my state object that has an array of objects inside. There are few things that I have tried but none of them worked. Here is an example of my state object. With the example below, I want to update the values for test and test2 depending on whatever the user types in input field. Can someone guide me in the right direction. Thank you!
this.state = {
someProperty: {
someOtherProperty: [
object in array: {
test: true,
test2: false
},
object in array: {
test: true
test2: false
}
..
]
...
}
...
}
My attempt on updating the value inside the object is as follows:
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: [
...prevState.someProperty.someOtherProperty,
test: false
]
}
}))
You can create an array latestSomeOtherProperty, update the wanted index and then;
const latestSomeOtherProperty = someOtherProperty.map(prop=>{...prop, test: false}); // it updates all but you can put condition here
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: [
...latestSomeOtherProperty
]
}
}))

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)

React setState to array element

I want to update list with React.
List I have:
export const arrList = [
{ title: '', elements: [] },
{ title: 'a', elements: ['aaa', ] },
{ title: 'b', elements: ['bbb', ] },
];
const [poreikiai, setPoreikiai] = useState(arrList);
And now, I want to append to first array element: elements { title: '', elements: [] }
I something like this:
{ title: '', elements: ["firstElement"] }
how can I do it?
I understant that I need to use setPoreikiai but I cant figure out sintax;
Something like:
setPoreikiai(prevState => {
return {...prevState, ...newValue};
});```
Deeply clone your way into the data structure and add the new item at the end:
setPoreikiai(prevState => {
const prevClone = [...prevState];
prevClone[0] = {
...prevClone[0],
elements: [...prevClone[0].elements, "secondItem"]
};
return prevClone;
});
It's important that you create copies all the way down to the desired level of state so that you do not accidently mutate any values by reference. Any directly mutated data can become out of sync with other effects and memoised components which might accept them as dependencies.

Update state of nested array with React Hooks

I have been working with React Hooks for a while, but the biggest problem for me is working with arrays.
How to update value of nested array of objects? I want to change dropdown type.
My code is below and changes the state directly. How can I fix this? What is the most correct code?
Thanks for your help.
const [grups, setGrups] = useState([
{
id: 1,
css: "col",
components: [
{ id: 1, type: "textbox" },
{ id: 2, type: "integer" },
],
},
{
id: 2,
css: "col",
components: [
{ id: 1, type: "numerictextbox" },
**{ id: 2, type: "dropdown" },**
],
},
]);
function handleClick(gindex,cindex) {
const newgrups = [...grups];
newgrups[gindex] = {...grups[gindex] ,components: [...grups[gindex].components]};
newgrups[gindex].components[cindex].tip="datetime";
setGrups(newgrups);
}
So you need something like
function handleClick(gindex, cindex) {
// use map to create a new array and at the same time
// modify the contents when required
const newgrups = grups.map((grup, gidx) => {
// if the index is not the one we want return the while grup as is
if (gidx !== gindex) return grup;
// otherwise create a new one by spreading the existing
return {
...grup,
// and override the prop which is changed
components: grup.components.map((component, cidx) => {
// again if the component is not the one we want to update
// return it as is
if (cidx !== cindex) return component;
// otherwise create a new one by spreading the existing
// and adding/modifying the props we want
return {
...component,
tip: 'datetime'
}
})
}
});
setGrups(newgrups);
}
If you just want to go with the code you have, you just need to create a new component as well
newgrups[gindex].components[cindex] = { ...newgrups[gindex].components[cindex],
tip: 'datetime'
}
It is good practice to design your state as you would design a database. In a database normalised design is recommended because you can't just nest states in other states. In your case you could split the state in the same way:
grups
[
{
id: 1,
css: "col"
},
{
id: 2,
css: "col"
}
];
components
[
{ grups_id: 1, id: 1, type: "textbox" },
{ grups_id: 1, id: 2, type: "integer" },
{ grups_id: 2, id: 1, type: "numerictextbox" },
{ grups_id: 2, id: 2, type: "dropdown" }
]
This makes it significantly easier to handle the state and avoid nesting and complex procedues. You can easily work with components by applying componenents.filter() and filter by grups_id.

Resources