How to update nested state properties in React
In order to setState
for a nested object you can follow the below approach as I think setState doesn't handle nested updates.
var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})
The idea is to create a dummy object perform operations on it and then replace the component's state with the updated object
Now, the spread operator creates only one level nested copy of the object. If your state is highly nested like:
this.state = {
someProperty: {
someOtherProperty: {
anotherProperty: {
flag: true
}
..
}
...
}
...
}
You could setState using spread operator at each level like
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: {
...prevState.someProperty.someOtherProperty,
anotherProperty: {
...prevState.someProperty.someOtherProperty.anotherProperty,
flag: false
}
}
}
}))
However the above syntax get every ugly as the state becomes more and more nested and hence I recommend you to use immutability-helper
package to update the state.
See this answer on how to update state with immutability-helper
.
Update Nested Objects in React Native using setState
There were few issues:
JSX component name
editNested
should start with a capital letter.And
editNested
component should be on its own function, should not define inside another component which caused yourTextInput
to lose focus after each render cycle.The
setState
call should be changed like below:
const updateNestedObject = (text) => {
setState((prevState) => ({
...prevState,
nested: prevState.nested.map((item) =>
item.id === nestedObject.id ? { ...item, value: text } : item
)
}));
};
Try the code below:
import React, { useState } from "react";
import { View, TextInput, Text, StyleSheet, Button, Alert } from "react-native";
function EditNested({ nestedObject, setState }) {
const updateNestedObject = (text) => {
setState((prevState) => ({
...prevState,
nested: prevState.nested.map((item) =>
item.id === nestedObject.id ? { ...item, value: text } : item
)
}));
};
return (
<View>
<Text>{nestedObject.title}</Text>
<TextInput
style={styles.input}
onChangeText={updateNestedObject}
value={nestedObject.value}
/>
</View>
);
}
function ObjectScreen() {
const [state, setState] = useState({
id: 1,
name: "Test Object",
nested: [
{
id: 1,
title: "Object 1",
value: ""
},
{
id: 2,
title: "Object 1",
value: ""
}
]
});
console.log(state);
return (
<>
<Text>{state.name}</Text>
{state.nested.map((nestedObject, key) => {
return (
<EditNested
key={key}
nestedObject={nestedObject}
setState={setState}
/>
);
})}
</>
);
}
const styles = StyleSheet.create({
container: {},
input: {
height: 40,
margin: 12,
borderWidth: 1,
padding: 10
}
});
export default ObjectScreen;
Working Demo
update nested state object with array inside
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
]
}
}))
Updating deeply nested state with useState not working properly
You're breaking one of the primary rules of React state: You're modifying a state object directly, rather than making a copy.
To correctly do the update, you'd do this:
const select = (e) => {
const i = e.target.getAttribute('data-index');
setItems((prevState) => {
// Copy the array (your code was doing that)
const update = [...prevState];
const item = update[i];
// Copy the object (your code wasn't doing that) and update its
// `selected` property
update[i] = {...item, selected: !item.selected};
return update;
});
};
Note how both the array and the object are copied, rather than just the array.
Updating Nested Object in React State
If it is only for 1 level nested object, you could try the following:
onChangeNest = (e) => {
let targetName = e.target.name
if (targetName.includes(".")) {
let splt = targetName.split(".")
let oldObj = {...this.state[splt[0]]}
oldObj[splt[1]] = e.target.value
this.setState({ [splt[0]]: oldObj })
} else {
this.setState({ [targetName]: e.target.value })
}
}
How to update nested object state in React
The problem is in onPress
, you are mutating the option
state directly. Because of that the option
state keeps the same reference in memory. Now even if you change something, react does not know if it should re-render and decides to nor re-render.
The solution to this problem is to create a copy of the state, so that we have a new reference to work with. In this copy we can modify it as we like and then set the new reference as option
state. Now react re-renders since it detects a new reference.
The code could look like this:
onPress={() =>
setOption(oldOption => {
const newOptions = [...oldOption];
newOptions[i] = { ...newOptions[i], isSelected: !newOptions[i].isSelected};
return newOptions;
})
Related Topics
Generating Random Whole Numbers in JavaScript in a Specific Range
How to Set/Unset a Cookie With Jquery
How to Use Multiple Versions of Jquery on the Same Page
Difference Between == and === in JavaScript
React Setstate Not Updating State
How to "Properly" Create a Custom Object in JavaScript
How to Access Post Form Fields in Express
How to Distinguish Between Left and Right Mouse Click With Jquery
How to Create an Object Property from a Variable Value in JavaScript
Use of 'Prototype' Vs. 'This' in JavaScript
How to Detect If a Browser Window Is Not Currently Active
Calculate Text Width With JavaScript
How to Completely Uninstall Node.Js, and Reinstall from Beginning (Mac Os X)
How to Get a Subset of a JavaScript Object'S Properties