Réagir: Comment puis-je mettre à jour l'état.l'article[1] sur setState? (avec JSFiddle)
Je suis la création d'une application où l'utilisateur peut concevoir son propre formulaire. E. g. spécifier le nom du champ et des détails sur d'autres colonnes qui doivent être inclus.
Le composant est disponible comme un JSFiddle ici.
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 veux mettre à jour l'état lorsque l'utilisateur modifie une de ces valeurs, mais je vais avoir un moment difficile de cibler le bon objet:
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 l'artisanat this.setState
à obtenir pour la mise à jour items[1].name
?
- Il n'y a aucune indication de la manière dont le
PopulateAtCheckboxes
composant dans votre édité question est liée à laDynamicForm
composant qui tient l'état vous êtes en train d'éditer. - veuillez voir mon code mis à jour.
- lié - stackoverflow.com/q/26253351/104380
Vous devez vous connecter pour publier un commentaire.
Vous pouvez utiliser le
update
immutabilité aide pour cela:Ou si vous ne vous inquiétez pas au sujet d'être en mesure de détecter les changements à ce point dans un
shouldComponentUpdate()
cycle de vie de la méthode à l'aide de===
, vous pouvez modifier l'état et de la force de la composante de re-rendre - c'est effectivement la même chose que @limelights réponse, comme c'est en tirant un objet hors de l'état et de l'éditer.Post-éditer plus:
Découvrez la Simple Composante De La Communication leçon de réagir-formation pour un exemple de la façon de passer d'une fonction de rappel à partir d'un état de rétention d'un parent à un enfant composant qui doit déclencher un changement d'état.
update
soulevé cette erreur:Uncaught ReferenceError: update is not defined
React.addons.update
Uncaught TypeError: Cannot read property 'items' of null
this
comme un accessoire<PopulateAtCheckboxes this={this}
et mis à jourthis.setState({ items: React.addons.update(this.props.this.state.items, {1: {name: {$set: 'updated field name'}} }) });
immutability-helper
n'est pas une performance roi. Voir ma réponse pour les résultats des tests.$splice
de commande.this.setState({ fields: update(this.state.fields, {$splice: [[index, 1, field]] }), field: null });
; ce qui signifie: "supprimer l'élément àindex
et d'ajouter de nouveaux à la même place."this.setState((state, props) => ...
Comme expliqué iciupdate
est un héritage add-on. Utiliser les immutabilité-helper à la place. Voir le lien à cette réponse pour plus de détailsMauvais sens!
Comme l'ont souligné beaucoup des meilleurs développeurs dans les commentaires: la mutation de l'état est mauvais!
M'a fallu un certain temps pour comprendre cela. Ci-dessus fonctionne, mais il ôte le pouvoir de Réagir. Par exemple
componentDidUpdate
ne verrez pas cela comme une mise à jour parce qu'il est modifié directement.Donc le droit chemin serait:
items[1].role = e.target.value
la mutation de l'état directement?this.state.items[1].role = e.target.value;
. Vous devez savoir ce qui est faux. Il est très déroutant qu'il a tellement de upvotes. Vous devez supprimer cette réponse.item
n'est pas défini dans votre "droit chemin"Car il y a beaucoup de désinformation dans ce fil, voici comment vous pouvez le faire sans helper libs:
Vous pouvez combiner les étapes 2 et 3 si vous le souhaitez:
Ou vous pouvez faire la même chose en une seule ligne:
Note: j'ai fait
items
un tableau. OP utilisé un objet. Cependant, les concepts sont les mêmes.Vous pouvez voir ce qui se passe dans votre terminal/console:
items[0] === clone[0]
peu dans mon exemple dans un terminal en bas. Triple=
vérifie si les objets font référence à la même chose.items.findIndex()
devrait faire qu'une bouchée de cette.À modifier profondément imbriqués les objets/variables dans le Réagissent de l'état, en général, trois méthodes sont utilisées: la vanille JS
Object.assign
, l'immutabilité-helper etcloneDeep
de Lodash. Il y a aussi beaucoup d'autres moins populaires tiers libs pour atteindre cet objectif, mais dans cette réponse, je vais couvrir seulement ces trois options. Aussi, il existe quelques méthodes qui utilisent rien d'autre que la vanille JavaScript, comme la matrice de diffusion, (voir @mpen, en réponse par exemple), mais ils ne sont pas très intuitif, facile à utiliser et capable de gérer toutes les manipulation de l'état situations.Comme l'a souligné à maintes reprises dans le top voté commentaires, réponses, dont les auteurs proposent une mutation directe de l'etat, il suffit de ne pas le faire. C'est un omniprésente Réagir anti-modèle, qui sera inévitablement vous conduire à des conséquences indésirables. Apprendre la bonne façon.
Nous allons comparer trois méthodes couramment utilisées.
Compte tenu de cette structure de l'etat:
1. Vanille JavaScript de l'Objet.attribuer
Gardez à l'esprit que Objet.attribuer de ne pas exécuter une profonde clonage, depuis il copie seulement les valeurs de propriété, et c'est pourquoi ce qu'il fait est appelé un peu profonde de la copie (voir les commentaires).
Pour que cela fonctionne, il faut manipuler cet objet de niveau supérieur éléments (
state.foo
). Et leurs valeurs (state.foo.bar
) doit être primitive (chaînes de caractères, nombres, booléens).Dans cet exemple, nous créons une nouvelle constante (
const foo...
), à l'aide deObject.assign
, ce qui crée un objet vide ({}
), des copiesstate.foo
objet ({ bar: 'initial value' }
), et ensuite des copies d'un objet différent{ bar: 'further value' }
sur elle. Donc, en fin de compte, le nouvellement crééfoo
constante tiendra une valeur de{ bar: 'further value' }
depuis lebar
propriété ai remplacée. Cettefoo
est une nouvelle marque de l'objet, qui n'est pas lié à l'état d'objet, de sorte qu'il peut être muté selon les besoins et l'état ne changera pas.La dernière partie est d'utiliser
setState()
setter pour remplacer l'originalstate.foo
dans l'état nouvellement crééfoo
objet.Maintenant, imaginons que nous ayons une plus profonde de l'état comme
state = { foo: { bar: { baz: 'initial value' } } }
. Nous pourrions essayer de créer un nouveaufoo
objet et de le remplir avec lefoo
contenu de l'état, maisObject.assign
ne sera pas en mesure de copierbaz
de la valeur à ce nouvellement crééfoo
objet depuisbaz
est imbriqué trop profonde. Vous pouvez toujours copierbar
, comme dans l'exemple ci-dessus, mais comme c'est un objet et non primitive, la référence destate.foo.bar
sera copié à la place, ce qui signifie que nous allons nous retrouver avec localfoo
objet directement liée à l'état. Cela signifie que, dans ce cas, les mutations de l'créés localementfoo
affectera lastate.foo
objet, car ils sont en fait pointant vers la même chose.Object.assign
donc ne fonctionnera que si vous avez un moyen relativement simple à un seul niveau de profondeur la structure de l'état avec le plus profond membres titulaires de valeurs de type primitif.Si vous avez plus profond des objets (2e niveau ou plus), que vous devriez le mettre à jour, ne pas utiliser de
Object.assign
. Vous risquez de la mutation de l'état directement.2. Lodash de cloneDeep
Lodash de cloneDeep est beaucoup plus simple à utiliser. Il effectue profonde clonage, c'est donc une solide option si vous avez une assez complexe de l'état avec multi-niveau des objets ou des tableaux à l'intérieur. Juste
cloneDeep()
le niveau supérieur de la propriété de l'etat, la mutation de l'cloné partie de ce que vous s'il vous plaît, etsetState()
il de revenir à l'état.3. immutabilité-helper
immutability-helper
s'en prend à un tout nouveau niveau, et le truc cool, c'est qu'elle ne peut pas seulement$set
valeurs de l'état des éléments, mais aussi$push
,$splice
,$merge
(etc.) eux. Voici une liste des commandes disponibles.Notes
Encore une fois, gardez à l'esprit, que
this.setState()
modifie seulement l' de premier niveau des propriétés de l'état de l'objet (foo
de propriété dans ce cas), et non pas profondément imbriqués (foo.bar
). Si il se comportait d'une autre manière, cette question n'existerait pas.Et par la manière,
this.setState({ foo })
est juste un raccourci pourthis.setState({ foo: foo })
. Et() => { console.log(this.state.foo.bar) }
après la{ foo }
est une fonction de callback qui est exécuté immédiatement aprèssetState
ont mis en l'état. Pratique, si vous avez besoin de faire certaines choses, après il a fait son travail (dans notre cas, pour afficher l'état immédiatement après qu'il a été défini).Qui est droit pour votre projet?
Si vous ne voulez pas ou ne pouvez utiliser dépendances externes, et ont un simple structure de l'état, s'en tenir à
Object.assign
.Si vous manipuler un immense et/ou complexe de l'état, Lodash de
cloneDeep
est un choix judicieux.Si vous avez besoin d' avancé capacités de, c'est à dire si votre état de la structure est complexe et vous avez besoin d'effectuer toutes sortes d'opérations sur elle, essayez de
immutability-helper
, c'est un outil très avancé qui peut être utilisé pour la manipulation de l'état.Object.assign
ne pas effectuer une copie en profondeur. Dire, sia = {c: {d: 1}}
etb = Object.assign({}, a)
, puis vous exécutezb.c.d = 4
, puisa.c.d
est muté.1
de l'objet plus profond (a.c.d
) va se muté. Mais si vous réaffectez le premier niveau successeur deb
, comme ceci:b.c = {f: 1}
, la partie correspondante dea
n'obtiendrez pas muté (il va rester{d: 1}
). Belle prise de toute façon, je vais mettre à jour tout de suite la réponse.shallow copy
et pas undeep copy
. Il est facile de confondre ce quishallow copy
moyens. Dansshallow copy
,a !== b
, mais pour chaque touche de la source de l'objeta
,a[key] === b[key]
Object.assign
dans la réponse.JSON.parse(JSON.stringify(object))
est aussi une variante de profondeur de clone. La performance est de pire lodashcloneDeep
bien. measurethat.net/Benchmarks/Show/2751/0/...D'abord obtenir l'élément que vous souhaitez, modifier ce que vous voulez sur l'objet et le ramener à l'état.
La façon dont vous êtes à l'aide de l'état par le seul passage d'un objet dans
getInitialState
serait beaucoup plus facile si vous utilisez un détrompeur objet.Uncaught TypeError: Cannot read property 'items' of null
.getInitialState
.getInitialState
? Pouvez-vous svp me donner un exemple?PopulateCheckboxes
n'a pas accès à votreDynamicForm
s de l'état. Vous avez besoin de passeritems
que les accessoires si vous voulez "access" qui.items
n'est pas définie n'importe où.item
une référence à l'étatthis.state.items[1]
variable. Ensuite, vous modifiezitem
(item.name = 'newName'
), et donc muter état directement, ce qui est très déconseillé. Dans votre exemple, il n'y a même pas besoin d'appelerthis.setState({items: items})
, parce que l'état est déjà muté directement.Ne pas muter l'état en place. Il peut provoquer des résultats inattendus. J'ai appris ma leçon! Toujours travailler avec un copier/cloner,
Object.assign()
est bonne:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
items
dans votre exemple? Vouliez-vous direthis.state.items
ou autre chose?J'ai eu le même problème. Voici une solution simple qui fonctionne !
{
accolades à la place de crochets qui j'avais remédiéSelon la Réagir la documentation sur setState, à l'aide de
Object.assign
comme suggéré par d'autres réponses ici n'est pas l'idéal. En raison de la nature desetState
's un comportement asynchrone, à la suite des appels à l'aide de cette technique peut remplacer les appels précédents provoquant des résultats indésirables.Au lieu de cela, la Réagir docs vous recommandons d'utiliser le formulaire de mise à jour de
setState
qui fonctionne à 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 Réagir nous oblige à préserver l'état de l'immuabilité. À l'aide de ES6 syntaxe de la propagation de l'opérateur de copie d'un tableau, la création ou la modification d'une propriété d'un objet à un index du tableau devrait ressembler à ceci:C'est vraiment simple.
Tirer d'abord l'ensemble des éléments de l'objet de l'état, mise à jour de la partie des éléments de l'objet tant désiré, et mettre l'ensemble des éléments de l'objet dans l'état via setState.
Mutation gratuit:
newItems
est réglé.Utiliser l'événement sur
handleChange
à déterminer l'élément qui a changé et puis la mise à jour. Pour que vous pourriez avoir besoin de modifier certaines propriétés de l'identifier et de le mettre à jour.Voir tripoter https://jsfiddle.net/69z2wepo/6164/
className
jsfiddle.net/69z2wepo/6165Essayez cela, il va certainement le travail,dans l'autre cas j'ai essayé, mais n'a pas de travail
Comment sur la création d'un autre composant(par objet qui doit aller dans le tableau) et passer à la suivante comme accessoires?
<SubObjectForm setData={this.setSubObjectData} objectIndex={index}/>
Ici {index} peut être transmise en fonction de la position où ce SubObjectForm est utilisé.
et setSubObjectData peut être quelque chose comme ça.
Dans SubObjectForm, cette.accessoires de jeu.setData peut être appelée sur des changements de données comme indiqué ci-dessous.
Si vous avez besoin de changer une partie seulement de la
Array
,Vous avez une réagir composant, avec état défini.
Il est préférable de mettre à jour le
red-one
dans leArray
comme suit:newArray
? voulez-vous direnewItems
? Si vous le faites, ne serait-ce pas quitter le territoire national avec un seul point après?newItems
sera[666]
: i.imgur.com/ttlyI6C.pngnewItems
à lastate
objet, et ne sera pas de mise à jour de l'existantitems
de la propriété.Comme aucune des options ci-dessus a été idéal pour moi, j'ai fini à l'aide de la carte:
Je voudrais déplacer la fonction gérer le changement et d'ajouter un paramètre index
à la Dynamique composant formulaire et le transmettre à la PopulateAtCheckboxes composant comme un accessoire. Comme vous vous en boucle sur vos articles, vous pouvez y ajouter un autre compteur (appelé indice dans le code ci-dessous) pour être transmis à la poignée de modifier comme indiqué ci-dessous
Enfin, vous pouvez appeler votre changement d'écoute comme indiqué ci-dessous ici
Essayer de mettre à jour votre
handleChange
comme suit -