Est-il possible de mettre à jour les propriétés d'un objet avec setState?
Quelque chose comme:
this.state = {
jasper: { name: 'jasper', age: 28 },
}
J'ai essayé:
this.setState({jasper.name: 'someOtherName'});
et ça:
this.setState({jasper: {name: 'someothername'}})
Le premier entraîne une erreur de syntaxe et le second ne fait rien. Des idées?
Il y a plusieurs façons de le faire, puisque la mise à jour de l'état est un opération asynchrone , donc pour mettre à jour l'objet d'état, nous devons utiliser fonction de mise à jour avec setState
.
1- Le plus simple:
Commencez par créer une copie de jasper
, puis apportez les modifications suivantes:
this.setState(prevState => {
let jasper = Object.assign({}, prevState.jasper); // creating copy of state variable jasper
jasper.name = 'someothername'; // update the name property, assign a new value
return { jasper }; // return new object jasper object
})
Au lieu d'utiliser Object.assign
, nous pouvons aussi l'écrire comme ceci:
let jasper = { ...prevState.jasper };
2- Utilisation de opérateur d'étalement:
this.setState(prevState => ({
jasper: { // object that we want to update
...prevState.jasper, // keep all other key-value pairs
name: 'something' // update the value of specific key
}
}))
Remarque: Object.assign
et Spread Operator
crée uniquement copie superficielle , donc si vous avez défini un objet imbriqué ou un tableau d'objets, vous avez besoin d'une approche différente.
Supposons que vous avez défini l'état comme suit:
this.state = {
food: {
sandwich: {
capsicum: true,
crackers: true,
mayonnaise: true
},
pizza: {
jalapeno: true,
extraCheese: false
}
}
}
Pour mettre à jour un objet extraCheese of pizza:
this.setState(prevState => ({
food: {
...prevState.food, // copy all other key-value pairs of food object
pizza: { // specific object of food object
...prevState.food.pizza, // copy all pizza key-value pairs
extraCheese: true // update value of specific key
}
}
}))
Supposons que vous avez une application à faire et que vous gérez les données sous cette forme:
this.state = {
todoItems: [
{
name: 'Learn React Basics',
status: 'pending'
}, {
name: 'Check Codebase',
status: 'pending'
}
]
}
Pour mettre à jour le statut de tout objet à faire, exécutez une mappe sur le tableau et recherchez une valeur unique pour chaque objet. Dans le cas de condition=true
, renvoyez le nouvel objet avec la valeur mise à jour, sinon le même objet.
let key = 2;
this.setState(prevState => ({
todoItems: prevState.todoItems.map(
el => el.key === key? { ...el, status: 'done' }: el
)
}))
Suggestion: Si l'objet n'a pas de valeur unique, utilisez l'index de tableau.
Le moyen le plus rapide et le plus lisible:
this.setState({...this.state.jasper, name: 'someothername'});
Bien que this.state.jasper
contienne déjà une propriété de nom, celle-ci name: 'someothername'
pouvant être utilisée.
Utilisez l'opérateur spread et un peu d'ES6 ici
this.setState({
jasper: {
...this.state.jasper,
name: 'something'
}
})
J'ai utilisé cette solution.
Si vous avez un état imbriqué comme ceci:
this.state = {
formInputs:{
friendName:{
value:'',
isValid:false,
errorMsg:''
},
friendEmail:{
value:'',
isValid:false,
errorMsg:''
}
}
vous pouvez déclarer la fonction handleChange qui copie l'état actuel et le réaffecte avec les valeurs modifiées
handleChange(el) {
let inputName = el.target.name;
let inputValue = el.target.value;
let statusCopy = Object.assign({}, this.state);
statusCopy.formInputs[inputName].value = inputValue;
this.setState(statusCopy);
}
voici le html avec l'écouteur d'événement. Assurez-vous d'utiliser le même nom que celui utilisé dans l'objet state (dans ce cas, 'FriendName')
<input type="text" onChange={this.handleChange} " name="friendName" />
essayez ceci, cela devrait fonctionner correctement
this.setState(Object.assign(this.state.jasper,{name:'someOtherName'}));
Le premier cas est en effet une erreur de syntaxe.
Étant donné que je ne vois pas le reste de votre composant, il est difficile de comprendre pourquoi vous imbriquez des objets dans votre état ici. Ce n'est pas une bonne idée d'imbriquer des objets dans l'état composant. Essayez de définir votre état initial pour être:
this.state = {
name: 'jasper',
age: 28
}
Ainsi, si vous souhaitez mettre à jour le nom, vous pouvez simplement appeler:
this.setState({
name: 'Sean'
});
Cela permettra-t-il d'atteindre ce que vous visez?
Pour les magasins de données plus grands et plus complexes, j'utiliserais quelque chose comme Redux. Mais c'est beaucoup plus avancé.
La règle générale avec l'état du composant consiste à l'utiliser uniquement pour gérer l'état de l'interface utilisateur du composant (par exemple, actif, minuteries, etc.).
Découvrez ces références:
Vous pouvez essayer avec ceci:
this.setState(prevState => {
prevState = JSON.parse(JSON.stringify(this.state.jasper));
prevState.name = 'someOtherName';
return {jasper: prevState}
})
ou pour d'autres biens:
this.setState(prevState => {
prevState = JSON.parse(JSON.stringify(this.state.jasper));
prevState.age = 'someOtherAge';
return {jasper: prevState}
})
Ou vous pouvez utiliser la fonction handleChage:
handleChage(event) {
const {name, value} = event.target;
this.setState(prevState => {
prevState = JSON.parse(JSON.stringify(this.state.jasper));
prevState[name] = value;
return {jasper: prevState}
})
}
et code HTML:
<input
type={"text"}
name={"name"}
value={this.state.jasper.name}
onChange={this.handleChange}
/>
<br/>
<input
type={"text"}
name={"age"}
value={this.state.jasper.age}
onChange={this.handleChange}
/>
En outre, selon la solution Alberto Piras, si vous ne voulez pas copier tout l’objet "state":
handleChange(el) {
let inputName = el.target.name;
let inputValue = el.target.value;
let jasperCopy = Object.assign({}, this.state.jasper);
jasperCopy[inputName].name = inputValue;
this.setState({jasper: jasperCopy});
}
Manière simple et dynamique.
Cela fera l'affaire, mais vous devez définir tous les identifiants sur le parent afin que celui-ci pointe vers le nom de l'objet, id = "jasper" et nomme le nom de l'élément input = property à l'intérieur de l'objet jasper. .
handleChangeObj = ({target: { id , name , value}}) => this.setState({ [id]: { ...this.state[id] , [name]: value } });
Vous pouvez essayer avec cela: (Note: nom de la balise d'entrée === champ de l'objet)
<input name="myField" type="text"
value={this.state.myObject.myField}
onChange={this.handleChangeInpForm}></input>
-----------------------------------------------------------
handleChangeInpForm = (e) => {
let newObject = this.state.myObject;
newObject[e.target.name] = e.target.value;
this.setState({
myObject: newObject
})
}
C'est ma fonction handleInputChange. Cela fonctionne de manière générique pour tout type d'objet. Cependant, le
l'utilisation d'eval est fortement déconseillée
handleInputChange(event) {
const target = event.target;
const targetPropertyName = target.name.substr(target.name.indexOf('.'));
const targetObjectName = target.name.substr(0, target.name.indexOf('.'));
const value = target.value;
var obj = this.state[targetObjectName];
eval('obj' + targetPropertyName + '= value');
this.setState({
[targetObjectName]: obj
});
}
Et ce mon formulaire de saisie
<input id="inputEmail" type="email" value={this.state.user.email} name="user.email" onChange={this.handleInputChange}/>
Des idées pour remplacer la fonction eval?
Une autre option: définissez votre variable à partir de l'objet Jasper , puis appelez simplement une variable.
opérateur de propagation: ES6
this.state = { jasper: { name: 'jasper', age: 28 } }
let foo = "something that needs to be saved into state"
this.setState(prevState => ({
jasper: {
...jasper.entity,
foo
}
})
Sans utiliser Async et Wait, utilisez ceci ...
funCall(){
this.setState({...this.state.jasper, name: 'someothername'});
}
Si vous utilisez Async And Wait, utilisez ceci ...
async funCall(){
await this.setState({...this.state.jasper, name: 'someothername'});
}