React: comment mettre à jour state.item [1] dans l'état en utilisant setState?


260

Je crée une application où l'utilisateur peut concevoir son propre formulaire. Par exemple, spécifiez le nom du champ et les détails des autres colonnes à inclure.

Le composant est disponible ici en tant que JSFiddle .

Mon état initial ressemble à ceci:

var DynamicForm = React.createClass({
  getInitialState: function() {
   var items = {};
   items[1] = { name: 'field 1', populate_at: 'web_start',
                same_as: 'customer_name',
                autocomplete_from: 'customer_name', title: '' };
   items[2] = { name: 'field 2', populate_at: 'web_end',
                same_as: 'user_name', 
                    autocomplete_from: 'user_name', title: '' };

     return { items };
   },

  render: function() {
     var _this = this;
     return (
       <div>
         { Object.keys(this.state.items).map(function (key) {
           var item = _this.state.items[key];
           return (
             <div>
               <PopulateAtCheckboxes this={this}
                 checked={item.populate_at} id={key} 
                   populate_at={data.populate_at} />
            </div>
            );
        }, this)}
        <button onClick={this.newFieldEntry}>Create a new field</button>
        <button onClick={this.saveAndContinue}>Save and Continue</button>
      </div>
    );
  }

Je souhaite mettre à jour l'état lorsque l'utilisateur modifie l'une des valeurs, mais j'ai du mal à cibler l'objet correct:

var PopulateAtCheckboxes = React.createClass({
  handleChange: function (e) {
     item = this.state.items[1];
     item.name = 'newName';
     items[1] = item;
     this.setState({items: items});
  },
  render: function() {
    var populateAtCheckbox = this.props.populate_at.map(function(value) {
      return (
        <label for={value}>
          <input type="radio" name={'populate_at'+this.props.id} value={value}
            onChange={this.handleChange} checked={this.props.checked == value}
            ref="populate-at"/>
          {value}
        </label>
      );
    }, this);
    return (
      <div className="populate-at-checkboxes">
        {populateAtCheckbox}
      </div>
    );
  }
});

Comment dois-je créer this.setStatepour le mettre à jour items[1].name?



1
quelle est votre réponse choisie pour cette question?
Braian Mellor

Réponses:


126

Vous pouvez utiliser l' updateaide à l' immuabilité pour cela :

this.setState({
  items: update(this.state.items, {1: {name: {$set: 'updated field name'}}})
})

Ou si vous ne vous souciez pas de pouvoir détecter les modifications apportées à cet élément dans une shouldComponentUpdate()méthode de cycle de vie à l'aide ===, vous pouvez modifier l'état directement et forcer le rendu du composant - c'est en fait la même chose que la réponse de @limelights, car c'est retirer un objet de son état et le modifier.

this.state.items[1].name = 'updated field name'
this.forceUpdate()

Ajout post-édition:

Consultez la leçon sur la communication de composants simples de React-Training pour voir comment passer une fonction de rappel d'un parent détenteur d'état à un composant enfant qui doit déclencher un changement d'état.


126

Puisqu'il y a beaucoup de désinformation dans ce fil, voici comment vous pouvez le faire sans bibliothèques d'assistance:

handleChange: function (e) {
    // 1. Make a shallow copy of the items
    let items = [...this.state.items];
    // 2. Make a shallow copy of the item you want to mutate
    let item = {...items[1]};
    // 3. Replace the property you're intested in
    item.name = 'newName';
    // 4. Put it back into our array. N.B. we *are* mutating the array here, but that's why we made a copy first
    items[1] = item;
    // 5. Set the state to our new copy
    this.setState({items});
},

Vous pouvez combiner les étapes 2 et 3 si vous le souhaitez:

let item = {
    ...items[1],
    name: 'newName'
}

Ou vous pouvez tout faire en une seule ligne:

this.setState(({items}) => ({
    items: [
        ...items.slice(0,1),
        {
            ...items[1],
            name: 'newName',
        },
        ...items.slice(2)
    ]
}));

Remarque: j'ai fait itemsun tableau. OP a utilisé un objet. Cependant, les concepts sont les mêmes.


Vous pouvez voir ce qui se passe dans votre terminal / console:

 node
> items = [{name:'foo'},{name:'bar'},{name:'baz'}]
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ]
> clone = [...items]
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ]
> item1 = {...clone[1]}
{ name: 'bar' }
> item1.name = 'bacon'
'bacon'
> clone[1] = item1
{ name: 'bacon' }
> clone
[ { name: 'foo' }, { name: 'bacon' }, { name: 'baz' } ]
> items
[ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ] // good! we didn't mutate `items`
> items === clone
false // these are different objects
> items[0] === clone[0]
true // we don't need to clone items 0 and 2 because we're not mutating them (efficiency gains!)
> items[1] === clone[1]
false // this guy we copied

5
@TranslucentCloud Oh ouais, les méthodes d'aide sont définitivement agréables, mais je pense que tout le monde devrait savoir ce qui se passe sous le capot :-)
mpen

Pourquoi avons-nous besoin des étapes 2 et 3? Pourquoi ne pouvons-nous pas muter le clone directement après la première étape?
Evmorov

1
@Evmorov Parce que ce n'est pas un clone profond. L'étape 1 consiste simplement à cloner le tableau, pas les objets à l'intérieur. En d'autres termes, chacun des objets à l'intérieur du nouveau tableau "pointe vers" les objets existants en mémoire - la mutation de l'un entraînera la mutation de l'autre (ils sont un seul et même). Voir aussi le items[0] === clone[0]bit dans mon exemple de terminal en bas. Triple =vérifie si les objets se réfèrent à la même chose.
mpen

Cela devient plus compliqué si vous ne connaissez pas l'index de l'élément dans le tableau.
Ian Warburton

@IanWarburton Pas vraiment. items.findIndex()devrait faire court travail de cela.
mpen

92

Fausse route!

handleChange = (e) => {
    const { items } = this.state;
    items[1].name = e.target.value;

    // update state
    this.setState({
        items,
    });
};

Comme l'ont souligné beaucoup de meilleurs développeurs dans les commentaires: la mutation de l'état est mauvaise!

Cela m'a pris un certain temps pour comprendre cela. Ci-dessus fonctionne mais il enlève la puissance de React. Par exemple componentDidUpdate, ne verra pas cela comme une mise à jour car il est modifié directement.

Donc, la bonne façon serait:

handleChange = (e) => {
    this.setState(prevState => ({
        items: {
            ...prevState.items,
            [prevState.items[1].name]: e.target.value,
        },
    }));
};

25
ES6 juste parce que vous utilisez "const"?
nkkollaw

49
items[1].role = e.target.valueN’appelle- t-il pas directement l’État en mutation?
antony

28
vous mutez l'état, c'est totalement contre l'idée de maintenir l'état immuable comme le suggère réagir. Cela peut vous faire beaucoup souffrir dans une grande application.
ncubica

8
@MarvinVK, votre réponse dit "La meilleure pratique serait donc:" suivie de l'utilisation de "this.forceUpdate ();" ce qui n'est pas recommandé car il pourrait être remplacé par setState (), voir facebook.github.io/react/docs/react-component.html#state . Mieux vaut changer cela pour ne pas dérouter les futurs lecteurs.
James Z.

8
Je voulais juste souligner que beaucoup de commentaires ne sont plus pertinents en raison de modifications et que la bonne façon est en fait la bonne.
heez

50

Pour modifier des objets / variables profondément imbriqués dans l'état de React, généralement trois méthodes sont utilisées: vanilla JavaScript's Object.assign, immutability-helper et cloneDeepfrom Lodash .

Il existe également de nombreuses autres bibliothèques tierces moins populaires pour y parvenir, mais dans cette réponse, je couvrirai uniquement ces trois options. De plus, il existe des méthodes JavaScript vanille supplémentaires, comme l'étalement de tableaux (voir la réponse de @ mpen par exemple), mais elles ne sont pas très intuitives, faciles à utiliser et capables de gérer toutes les situations de manipulation d'état.

Comme cela a été souligné à maintes reprises dans les commentaires les plus votés des réponses, dont les auteurs proposent une mutation directe de l'État: ne faites pas cela . Il s'agit d'un anti-modèle omniprésent de React, qui entraînera inévitablement des conséquences indésirables. Apprenez de la bonne façon.

Comparons trois méthodes largement utilisées.

Étant donné cette structure d'objet d'état:

state = {
    outer: {
        inner: 'initial value'
    }
}

Vous pouvez utiliser les méthodes suivantes pour mettre à jour la innervaleur du champ le plus interne sans affecter le reste de l'état.

1. Object.assign de Vanilla JavaScript

const App = () => {
  const [outer, setOuter] = React.useState({ inner: 'initial value' })

  React.useEffect(() => {
    console.log('Before the shallow copying:', outer.inner) // initial value
    const newOuter = Object.assign({}, outer, { inner: 'updated value' })
    console.log('After the shallow copy is taken, the value in the state is still:', outer.inner) // initial value
    setOuter(newOuter)
  }, [])

  console.log('In render:', outer.inner)

  return (
    <section>Inner property: <i>{outer.inner}</i></section>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>

<main id="react"></main>

Gardez à l'esprit que Object.assign n'effectuera pas de clonage en profondeur , car il copie uniquement les valeurs de propriété , et c'est pourquoi ce qu'il fait est appelé une copie superficielle (voir les commentaires).

Pour que cela fonctionne, nous devons seulement manipuler les propriétés de types primitifs ( outer.inner), c'est-à-dire les chaînes, les nombres, les booléens.

Dans cet exemple, nous créons une nouvelle constante ( const newOuter...), en utilisant Object.assign, qui crée un objet vide ( {}), y copie un outerobjet ( { inner: 'initial value' }), puis copie un autre objet{ inner: 'updated value' } dessus .

De cette façon, à la fin, la newOuterconstante nouvellement créée conservera une valeur de { inner: 'updated value' }puisque leinner propriété a été remplacée. Il newOuters'agit d'un tout nouvel objet, qui n'est pas lié à l'objet en état, il peut donc être modifié selon les besoins et l'état restera le même et ne sera pas modifié jusqu'à ce que la commande de mise à jour soit exécutée.

La dernière partie consiste à utiliser setOuter()setter pour remplacer l'original outeren l'état par un nouveaunewOuter objet (seule la valeur changera, le nom de la propriété outerne changera pas).

Imaginez maintenant que nous avons un état plus profond comme state = { outer: { inner: { innerMost: 'initial value' } } }. Nous pourrions essayer de créer l' newOuterobjet et le remplir avec le outercontenu de l'état, mais Object.assignnous ne pourrons pas copierinnerMost la valeur de cet newOuterobjet nouvellement créé car il innerMostest imbriqué trop profondément.

Vous pouvez toujours copier inner, comme dans l'exemple ci-dessus, mais comme il s'agit désormais d'un objet et non d' une primitive, la référence de newOuter.innersera copiée dans leouter.inner , ce qui signifie que nous nous retrouverons avec un newOuterobjet local directement lié à l'objet dans l'état .

Cela signifie que dans ce cas, les mutations de la création locale newOuter.inner affecteront directement l' outer.innerobjet (en état), car elles sont en fait devenues la même chose (dans la mémoire de l'ordinateur).

Object.assign par conséquent, cela ne fonctionnera que si vous avez une structure d'état profond à un niveau relativement simple avec des membres les plus internes contenant des valeurs de type primitif.

Si vous avez des objets plus profonds (2e niveau ou plus) que vous devez mettre à jour, ne les utilisez pas Object.assign. Vous risquez de muter directement l'état.

2. CloneDeep de Lodash

const App = () => {
  const [outer, setOuter] = React.useState({ inner: 'initial value' })

  React.useEffect(() => {
    console.log('Before the deep cloning:', outer.inner) // initial value
    const newOuter = _.cloneDeep(outer) // cloneDeep() is coming from the Lodash lib
    newOuter.inner = 'updated value'
    console.log('After the deeply cloned object is modified, the value in the state is still:', outer.inner) // initial value
    setOuter(newOuter)
  }, [])

  console.log('In render:', outer.inner)

  return (
    <section>Inner property: <i>{outer.inner}</i></section>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

<main id="react"></main>

CloneDeep de Lodash est beaucoup plus simple à utiliser. Il effectue un clonage en profondeur , c'est donc une option robuste, si vous avez un état assez complexe avec des objets ou des tableaux à plusieurs niveaux à l'intérieur. JustecloneDeep() la propriété d'état de niveau supérieur, mute la partie clonée de la manière qui vous convient, et setOuter()retourne à l'état.

3. aide à l'immuabilité

const App = () => {
  const [outer, setOuter] = React.useState({ inner: 'initial value' })
  
  React.useEffect(() => {
    const update = immutabilityHelper
    console.log('Before the deep cloning and updating:', outer.inner) // initial value
    const newOuter = update(outer, { inner: { $set: 'updated value' } })
    console.log('After the cloning and updating, the value in the state is still:', outer.inner) // initial value
    setOuter(newOuter)
  }, [])

  console.log('In render:', outer.inner)

  return (
    <section>Inner property: <i>{outer.inner}</i></section>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('react')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
<script src="https://wzrd.in/standalone/immutability-helper@3.0.0"></script>

<main id="react"></main>

immutability-helperil faut à un nouveau niveau, et la chose cool à ce sujet est qu'il peut non seulement des $setvaleurs aux éléments de l' État, mais aussi $push, $splice, $merge(etc.) eux. Voici une liste de commandes disponibles.

Notes annexes

Encore une fois, gardez à l'esprit que cela setOuterne modifie que les propriétés de premier niveau de l'objet d'état ( outerdans ces exemples), pas le profondément imbriqué ( outer.inner). S'il se comportait différemment, cette question n'existerait pas.

Lequel est bon pour votre projet?

Si vous ne voulez pas ou ne pouvez pas utiliser de dépendances externes et que vous avez une structure d'état simple , respectez-la Object.assign.

Si vous manipulez un état énorme et / ou complexe , Lodash cloneDeepest un choix judicieux.

Si vous avez besoin de capacités avancées , c'est-à-dire si votre structure d'état est complexe et que vous devez y effectuer toutes sortes d'opérations, essayez immutability-helper, c'est un outil très avancé qui peut être utilisé pour la manipulation d'état.

... ou, avez-vous vraiment besoin de faire cela du tout?

Si vous détenez des données complexes dans l'état de React, c'est peut-être le bon moment pour réfléchir à d'autres façons de les gérer. La définition d'un état complexe d'objets directement dans les composants React n'est pas une opération simple, et je suggère fortement de réfléchir à différentes approches.

Il est fort probable que vous feriez mieux de ne pas conserver vos données complexes dans un magasin Redux, de les y placer à l'aide de réducteurs et / ou de sagas et d'y accéder à l'aide de sélecteurs.


Object.assignn'effectue pas de copie complète. Dites, si a = {c: {d: 1}}et b = Object.assign({}, a)alors vous exécutez b.c.d = 4, puis a.c.dest muté.
Awol

Vous avez raison, la valeur 1de l'objet le plus intérieur ( a.c.d) sera modifiée. Mais si vous réaffectez le successeur de premier niveau de b, comme ceci b.c = {f: 1}:, la partie correspondante de ane sera pas mutée (elle restera {d: 1}). Belle prise de toute façon, je mettrai à jour la réponse immédiatement.
Neurotransmetteur

Ce que vous avez défini est en fait un shallow copyet non un deep copy. Il est facile de confondre ce que cela shallow copysignifie. Dans shallow copy, a !== bmais pour chaque clé de l' objet source a,a[key] === b[key]
Awol

Oui, la superficialité explicitement mentionnée Object.assigndans la réponse.
Neurotransmetteur

JSON.parse(JSON.stringify(object))est également une variante du clone profond. La performance est pire que le lodash cloneDeep. Measurethat.net/Benchmarks/Show/2751/0/…
tylik

35

J'ai eu le même problème. Voici une solution simple qui fonctionne!

const newItems = [...this.state.items];
newItems[item] = value;
this.setState({ items:newItems });

11
@TranslucentCloud - ce n'est certainement pas une mutation directe. le tableau d'origine a été cloné, modifié, puis l'état a été à nouveau défini à l'aide du tableau cloné.
vsync

@vsync ouais, maintenant après avoir modifié la réponse originale, ce n'est pas du tout une mutation.
Neurotransmetteur

2
@TranslucentCloud - Avant aussi, mon montage n'a rien à voir avec ça, merci beaucoup pour l'attitude. @Jonas ici n'a fait qu'une simple erreur dans sa réponse, en utilisant {des accolades au lieu de crochets auxquels j'avais remédié
vsync

31

Selon la documentation React sur setState , l'utilisation Object.assigncomme suggéré par d'autres réponses ici n'est pas idéale. En raison de la nature du setStatecomportement asynchrone de, les appels ultérieurs utilisant cette technique peuvent remplacer les appels précédents et provoquer des résultats indésirables.

Au lieu de cela, les documents React recommandent d'utiliser la forme de mise à jour setStatequi fonctionne sur l'état précédent. Gardez à l'esprit que lors de la mise à jour d'un tableau ou d'un objet, vous devez renvoyer un nouveau tableau ou un objet car React nous oblige à préserver l'immuabilité de l'état. À l'aide de l'opérateur d'étalement de la syntaxe ES6 pour copier peu profondément un tableau, la création ou la mise à jour d'une propriété d'un objet à un index donné du tableau ressemblerait à ceci:

this.setState(prevState => {
    const newItems = [...prevState.items];
    newItems[index].name = newName;
    return {items: newItems};
})

2
C'est la réponse appropriée si vous utilisez ES6. C'est en ligne ce que @Jonas a répondu. Cependant, en raison de l'explication, il se démarque.
Sourabh

Oui, ce code fonctionne parfaitement bien et très simple ..
Shoeb Mirza

29

Obtenez d'abord l'élément que vous voulez, changez ce que vous voulez sur cet objet et remettez-le sur l'état. La façon dont vous utilisez l'état en ne passant qu'un objet getInitialStateserait beaucoup plus facile si vous utilisiez un objet à clé.

handleChange: function (e) {
   item = this.state.items[1];
   item.name = 'newName';
   items[1] = item;

   this.setState({items: items});
}

3
Non, ça cède Uncaught TypeError: Cannot read property 'items' of null.
martins

Non, ce ne sera pas le cas. Votre erreur provient probablement de votre façon de faire getInitialState.
Henrik Andersson

10
@HenrikAndersson Quelque chose ne semble pas correct dans votre exemple. itemsn'est défini nulle part.
Edward D'Souza

1
@ EdwardD'Souza Vous avez absolument raison! Ma réponse est de montrer comment il doit être défini et utilisé. La façon dont le code du demandeur est configuré ne fonctionne pas pour la chose qu'il souhaite, c'est pourquoi le besoin d'un objet à clé est requis.
Henrik Andersson

7
Il s'agit d'un anti-modèle React typique, vous attribuez itemune référence à la propre this.state.items[1]variable de l'état . Ensuite, vous modifiez item( item.name = 'newName'), et donc mute directement l'état, ce qui est fortement déconseillé. Dans votre exemple, il n'est même pas nécessaire d'appeler this.setState({items: items}), car l'état est déjà directement muté.
Neurotransmetteur

22

Ne mute pas l'état en place. Cela peut entraîner des résultats inattendus. J'ai appris ma leçon! Toujours travailler avec une copie / un clone, Object.assign()c'est un bon:

item = Object.assign({}, this.state.items[1], {name: 'newName'});
items[1] = item;
this.setState({items: items});

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign


8
quel est itemsvotre exemple? Voulez-vous dire this.state.itemsou autre chose?
Buh Buh

J'ai testé cela mais il lui manque une ligne. Au-dessus, items[1] = item;il devrait y avoir une ligne disant items = this.state.items;. Attention mon javascript est rouillé et j'apprends à réagir pour mon projet maison donc je n'ai aucune idée si c'est bon ou mauvais :-)
Greg0ry

4

C'est vraiment simple.

Commencez par extraire l'objet articles entier de l'état, mettez à jour la partie de l'objet articles comme vous le souhaitez et remettez l'objet articles entier en état via setState.

handleChange: function (e) {
  items = Object.assign(this.state.items); // Pull the entire items object out. Using object.assign is a good idea for objects.
  items[1].name = 'newName'; // update the items object as needed
  this.setState({ items }); // Put back in state
}

Msgstr "Object.assign fonctionnera si vous avez une structure d 'état profond à un niveau relativement simple avec des membres les plus internes contenant des valeurs de type primitif".
Neurotransmetteur

3

Comme aucune des options ci-dessus n'était idéale pour moi, j'ai fini par utiliser la carte:

this.setState({items: this.state.items.map((item,idx)=> idx!==1 ?item :{...item,name:'new_name'}) })

2

Sans mutation:

// given a state
state = {items: [{name: 'Fred', value: 1}, {name: 'Wilma', value: 2}]}

// This will work without mutation as it clones the modified item in the map:
this.state.items
   .map(item => item.name === 'Fred' ? {...item, ...{value: 3}} : item)

this.setState(newItems)

2
Je ne vois pas où newItemsest réglé.
Neurotransmetteur

Une carte plus une comparaison dans le tableau n'est-elle pas horrible pour les performances?
Natassia Tavares

@NatassiaTavares .. quoi? vous êtes peut-être confondu avec fo ofou la forEachcarte est la plus rapide.
Deano

1

J'ai trouvé cela étonnamment difficile et aucune magie de propagation d'ES6 ne semblait fonctionner comme prévu. J'utilisais une structure comme celle-ci pour obtenir les propriétés des éléments rendus à des fins de disposition.

trouvée en utilisant la updateméthode from immutability-helperpour être la plus simple dans cet exemple simplifié:

constructor(props) {
    super(props)
    this.state = { values: [] }
    this.updateContainerState = this.updateContainerState.bind(this)
  }

updateContainerState(index, value) {
    this.setState((state) => update(state, { values: { [index]: { $set: value } } }))
  }

adapté de https://github.com/kolodny/immutability-helper#computed-property-names

du membre du tableau à mettre à jour est un objet complexe plus imbriqué, utilisez la méthode de copie approfondie appropriée fonction de la complexité.

Il existe certainement de meilleures façons de gérer les paramètres de mise en page, mais il s'agit de gérer les tableaux. Les valeurs pertinentes pour chaque élément enfant peuvent également être calculées en dehors d'eux, mais j'ai trouvé plus pratique de passer containerState vers le bas, afin que les enfants puissent récupérer les propriétés à volonté et mettre à jour le tableau d'état parent à leur index donné.

import React from 'react'
import update from 'immutability-helper'
import { ContainerElement } from './container.component.style.js'
import ChildComponent from './child-component'
export default class ContainerComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = { values: [] }
    this.updateContainerState = this.updateContainerState.bind(this)
  }

  updateContainerState(index, value) {
    this.setState((state) => update(state, { values: { [index]: { $set: value } } }))
  }

  // ...

  render() {
    let index = 0
    return (
      <ContainerElement>
      <ChildComponent
        index={index++}
        containerState={this.state}
        updateContainerState={this.updateContainerState}
      />
      <ChildComponent
        index={index++}
        containerState={this.state}
        updateContainerState={this.updateContainerState}
      />
      </ContainerElement>
    )
  }
}

1

Utiliser la carte du tableau avec la fonction flèche, sur une seule ligne

this.setState({
    items: this.state.items.map((item, index) =>
      index === 1 ? { ...item, name: 'newName' } : item,
   )
})

1
Ceci est fondamentalement identique à cette autre réponse publiée il y a environ un an
CertainPerformance


0

Je déplacerais le changement de poignée de fonction et ajouterais un paramètre d'index

handleChange: function (index) {
    var items = this.state.items;
    items[index].name = 'newName';
    this.setState({items: items});
},

au composant de formulaire dynamique et le transmettre au composant PopulateAtCheckboxes en tant que prop. Lorsque vous parcourez vos articles, vous pouvez inclure un compteur supplémentaire (appelé index dans le code ci-dessous) à transmettre au changement de poignée, comme indiqué ci-dessous

{ Object.keys(this.state.items).map(function (key, index) {
var item = _this.state.items[key];
var boundHandleChange = _this.handleChange.bind(_this, index);
  return (
    <div>
        <PopulateAtCheckboxes this={this}
            checked={item.populate_at} id={key} 
            handleChange={boundHandleChange}
            populate_at={data.populate_at} />
    </div>
);
}, this)}

Enfin, vous pouvez appeler votre écouteur de changement comme indiqué ci-dessous ici

<input type="radio" name={'populate_at'+this.props.id} value={value} onChange={this.props.handleChange} checked={this.props.checked == value} ref="populate-at"/>

Ne modifiez jamais directement l'état de React.
Neurotransmetteur

0

Si vous ne devez modifier qu'une partie de la Array, vous avez un composant React dont l'état est défini sur.

state = {items: [{name: 'red-one', value: 100}, {name: 'green-one', value: 999}]}

Il est préférable de mettre à jour le red-onedans le Arraycomme suit:

const itemIndex = this.state.items.findIndex(i=> i.name === 'red-one');
const newItems = [
   this.state.items.slice(0, itemIndex),
   {name: 'red-one', value: 666},
   this.state.items.slice(itemIndex)
]

this.setState(newItems)

c'est quoi newArray? tu veux dire newItems? Si vous le faites, cela ne quitterait-il pas l'État avec un seul article par la suite?
micnil

Cela introduira une nouvelle propriété newItemsà l' stateobjet et ne mettra pas à jour la itemspropriété existante .
Neurotransmetteur

0

ou si vous avez une liste générée dynamiquement et que vous ne connaissez pas l'index mais que vous avez juste la clé ou l'id:

let ItemsCopy = []
let x = this.state.Items.map((entry) =>{

    if(entry.id == 'theIDYoureLookingFor')
    {
        entry.PropertyToChange = 'NewProperty'
    }

    ItemsCopy.push(entry)
})


this.setState({Items:ItemsCopy});

0

Essayez avec du code:

this.state.items[1] = 'new value';
var cloneObj = Object.assign({}, this.state.items);

this.setState({items: cloneObj });

0

Le morceau de code suivant est allé doucement sur mon cerveau terne. Suppression de l'objet et remplacement par celui mis à jour

    var udpateditem = this.state.items.find(function(item) { 
                   return item.name == "field_1" });
    udpateditem.name= "New updated name"                       
    this.setState(prevState => ({                                   
    items:prevState.dl_name_template.filter(function(item) { 
                                    return item.name !== "field_1"}).concat(udpateditem)
    }));

0

Que diriez-vous de créer un autre composant (pour un objet qui doit entrer dans le tableau) et de passer les éléments suivants comme accessoires?

  1. index des composants - l'index sera utilisé pour créer / mettre à jour dans le tableau.
  2. fonction set - Cette fonction place les données dans le tableau en fonction de l'index du composant.
<SubObjectForm setData={this.setSubObjectData}                                                            objectIndex={index}/>

Ici, {index} peut être transmis en fonction de la position où ce SubObjectForm est utilisé.

et setSubObjectData peut être quelque chose comme ça.

 setSubObjectData: function(index, data){
      var arrayFromParentObject= <retrieve from props or state>;
      var objectInArray= arrayFromParentObject.array[index];
      arrayFromParentObject.array[index] = Object.assign(objectInArray, data);
 }

Dans SubObjectForm, this.props.setData peut être appelé lors d'un changement de données comme indiqué ci-dessous.

<input type="text" name="name" onChange={(e) => this.props.setData(this.props.objectIndex,{name: e.target.value})}/>

0
this.setState({
      items: this.state.items.map((item,index) => {
        if (index === 1) {
          item.name = 'newName';
        }
        return item;
      })
    });

1
ce n'est donc pas optimal, vous itérez tout le tableau afin de ne mettre à jour que le deuxième élément?
wscourge

Vous mutez également l'élément du premier tableau, vous devez utiliseritem = Object.assign({}, item, {name: 'newName'});
remram

0

La réponse de @ JonnyBuchanan fonctionne parfaitement, mais uniquement pour la variable d'état du tableau. Dans le cas où la variable d'état n'est qu'un seul dictionnaire, procédez comme suit:

inputChange = input => e => {
    this.setState({
        item: update(this.state.item, {[input]: {$set: e.target.value}})
    })
}

Vous pouvez remplacer [input]par le nom de champ de votre dictionnaire et e.target.valuepar sa valeur. Ce code effectue la tâche de mise à jour lors de l'événement de modification d'entrée de mon formulaire.


-3

Essayez ceci, cela fonctionnera certainement, dans d'autres cas, j'ai essayé mais n'a pas fonctionné

import _ from 'lodash';

this.state.var_name  = _.assign(this.state.var_name, {
   obj_prop: 'changed_value',
});

Ne modifiez jamais directement l'état de React.
Neurotransmetteur

-3
 handleChanges = (value, key) => {
     // clone the current State object
    let cloneObject = _.extend({}, this.state.currentAttribute);
    // key as user.name and value= "ABC" then current attributes have current properties as we changes
    currentAttribute[key] = value;
    // then set the state "currentAttribute" is key and "cloneObject" is changed object.  
    this.setState({currentAttribute: cloneObject});

et modification de la zone de texte, ajouter l'événement onChange

onChange = {
   (event) => {                                                
      this.handleChanges(event.target.value, "title");
   }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.