web-dev-qa-db-fra.com

Mise à jour d'un objet avec setState dans React

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?

182
JohnSnow

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.


Mise à jour de l'objet d'état imbriqué:

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

Mise à jour du tableau d'objets:

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.

380
Mayank Shukla

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.

22
mannok

Utilisez l'opérateur spread et un peu d'ES6 ici

this.setState({
    jasper: {
          ...this.state.jasper,
          name: 'something'
    }
})
20
Nikhil

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" />
7
Alberto Piras

essayez ceci, cela devrait fonctionner correctement

this.setState(Object.assign(this.state.jasper,{name:'someOtherName'}));
6
Burak Kahraman

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:

3
mccambridge

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}
/>
1
Nemanja Stojanovic

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});
  }
0
Marc LaQuay

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 
   })
}
0
Xuanduong Ngo

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?

0
juan

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
    }
})
0
Luis Martins

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'});
}
0
Shantanu Sharma