Comment mettre à jour les propriétés des états imbriqués dans React

390

J'essaie d'organiser mon état en utilisant une propriété imbriquée comme ceci:

this.state = {
   someProperty: {
      flag:true
   }
}

Mais la mise à jour de l'état comme ça,

this.setState({ someProperty.flag: false });

ne fonctionne pas. Comment cela peut-il être fait correctement?

Alex Yong
la source
4
Que voulez-vous dire ne fonctionne pas? Ce n'est pas une très bonne question - que s'est-il passé? Des erreurs? Quelles erreurs?
Darren Sweeney
Essayez de lire: stackoverflow.com/questions/18933985/…
Andrew Paramoshkin
16
La réponse est Ne pas utiliser l'état imbriqué dans React . Lisez cette excellente réponse.
Qwerty
4
Que faut-il utiliser à la place?
Jānis Elmeris
42
Le fait de ne pas utiliser l'état imbriqué est tout simplement une réponse inacceptable pour l'étendue de l'utilisation actuelle de React. Cette situation va se produire et les développeurs ont besoin d'une réponse à cela.
serraosays

Réponses:

456

Pour setStateun objet imbriqué, vous pouvez suivre l'approche ci-dessous car je pense que setState ne gère pas les mises à jour imbriquées.

var someProperty = {...this.state.someProperty}
someProperty.flag = true;
this.setState({someProperty})

L'idée est de créer un objet factice, d'effectuer des opérations dessus, puis de remplacer l'état du composant par l'objet mis à jour

Désormais, l'opérateur de propagation crée une seule copie imbriquée de niveau de l'objet. Si votre état est fortement imbriqué comme:

this.state = {
   someProperty: {
      someOtherProperty: {
          anotherProperty: {
             flag: true
          }
          ..
      }
      ...
   }
   ...
}

Vous pouvez définirState en utilisant l'opérateur de propagation à chaque niveau, comme

this.setState(prevState => ({
    ...prevState,
    someProperty: {
        ...prevState.someProperty,
        someOtherProperty: {
            ...prevState.someProperty.someOtherProperty, 
            anotherProperty: {
               ...prevState.someProperty.someOtherProperty.anotherProperty,
               flag: false
            }
        }
    }
}))

Cependant, la syntaxe ci-dessus devient laide à mesure que l'état devient de plus en plus imbriqué et je vous recommande donc d'utiliser le immutability-helperpackage pour mettre à jour l'état.

Consultez cette réponse pour savoir comment mettre à jour l'état avec immutability helper.

Shubham Khatri
la source
2
@Ohgodwhy, non, cela n'accède pas directement à l'état puisque {... this.state.someProperty} retunit un nouvel objet somePropertyet nous le
modifions
4
cela n'a pas fonctionné pour moi .. j'utilise la version react @ 15.6.1
Rajiv
5
@Stophface Nous pouvons utiliser Lodash pour cloner en profondeur bien sûr, mais tout le monde n'inclurait pas cette bibliothèque juste pour setState
Shubham Khatri
15
Cela ne viole-t-il pas reactjs.org/docs/… ? Si deux modifications apportées à l'objet imbriqué étaient groupées, la dernière modification écraserait la première. Donc, le moyen le plus correct serait: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) Un peu fastidieux
quand
2
Je ne pense pas que vous ayez besoin de ...prevState,dans le dernier exemple, vous seul somePropertyet ses enfants
Jemar Jones
140

Pour l'écrire sur une seule ligne

this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
Yoseph
la source
12
Il est recommandé d'utiliser le programme de mise à jour pour setState au lieu d'accéder directement à this.state dans setState. reactjs.org/docs/react-component.html#setstate
Raghu Teja
6
Je crois que cela dit, ne lisez pas this.stateimmédiatement après setStateet attendez-vous à ce qu'il ait la nouvelle valeur. Versus appelant à l' this.stateintérieur setState. Ou y a-t-il également un problème avec ce dernier qui n'est pas clair pour moi?
trpt4him
2
Comme l'a dit trpt4him , le lien que vous avez donné parle du problème d'accès this.stateaprès setState. D'ailleurs, nous ne passons pas this.stateà setState. L' opérateur répartit les propriétés. C'est la même chose que Object.assign (). github.com/tc39/proposal-object-rest-spread
Yoseph
3
@RaghuTeja peut-être juste faire this.setState(currentState => { someProperty: { ...currentState.someProperty, flag: false} });pourrait permettre d'éviter ce problème?
Webwoman
J'ai ajouté la réponse comment obtenir le même résultat avec le crochet useState pour être complet.
Andrew
90

Parfois, les réponses directes ne sont pas les meilleures :)

Version courte:

ce code

this.state = {
    someProperty: {
        flag: true
    }
}

devrait être simplifié comme quelque chose comme

this.state = {
    somePropertyFlag: true
}

Version longue:

Actuellement, vous ne devriez pas vouloir travailler avec un état imbriqué dans React . Parce que React n'est pas orienté pour fonctionner avec des états imbriqués et toutes les solutions proposées ici ressemblent à des hacks. Ils n'utilisent pas le cadre mais se battent avec lui. Ils suggèrent d'écrire un code moins clair dans le but douteux de regrouper certaines propriétés. Ils sont donc très intéressants comme réponse au défi mais pratiquement inutiles.

Imaginons l'état suivant:

{
    parent: {
        child1: 'value 1',
        child2: 'value 2',
        ...
        child100: 'value 100'
    }
}

Que se passera-t-il si vous modifiez uniquement une valeur de child1? React ne restitue pas la vue car il utilise une comparaison superficielle et il constate que la parentpropriété n'a pas changé. BTW muter directement l'objet d'état est considéré comme une mauvaise pratique en général.

Vous devez donc recréer l' parentobjet entier . Mais dans ce cas, nous rencontrerons un autre problème. React pensera que tous les enfants ont changé leurs valeurs et les restituera tous. Bien sûr, ce n'est pas bon pour la performance.

Il est toujours possible de résoudre ce problème en écrivant une logique compliquée, shouldComponentUpdate()mais je préférerais m'arrêter ici et utiliser une solution simple à partir de la version courte.

Konstantin Smolyanin
la source
4
Je pense qu'il existe des cas d'utilisation où un état imbriqué convient parfaitement. Par exemple, un état effectuant un suivi dynamique des paires clé-valeur. Voir ici comment mettre à jour l'état pour une seule entrée dans l'état: reactjs.org/docs/…
pors
1
La comparaison superficielle est utilisée par défaut pour les composants purs.
Basel Shishani
4
Je veux créer un autel en votre honneur et le prier chaque matin. Il m'a fallu trois jours pour arriver à cette réponse qui explique parfaitement la décision de conception OBVIOUS. Tout le monde essaie simplement d'utiliser l'opérateur de propagation ou de faire d'autres piratages simplement parce que l'état imbriqué semble plus lisible par l'homme.
Edeph
1
Comment proposez-vous alors, d'utiliser React pour faire des choses comme rendre un arbre interactif (par exemple, mon entreprise a un tas de capteurs dans différentes parties du monde, chacun de type différent, avec des propriétés différentes, dans des endroits différents (80+ ), et bien sûr, ils sont organisés en hiérarchie car chaque déploiement coûte des milliers de dollars et est un projet complet, il serait donc impossible de les gérer sans hiérarchie). Je suis assez curieux de savoir comment vous ne modéliseriez pas des choses comme des arbres comme ... des arbres, dans React.
DanyAlejandro
10
semble que React n'a pas été fait pour représenter quoi que ce soit qui est stocké comme une structure récursive de profondeur non triviale. C'est un peu décevant car des arborescences apparaissent dans presque toutes les applications complexes (gmail, gestionnaires de fichiers, tout système de gestion de documents, ..). J'ai utilisé React pour ces choses, mais j'ai toujours eu un fanatique de React disant à tout le monde que vous faites des anti-modèles et suggérant que la solution consiste à conserver des copies profondes de l'arbre dans l'état de chaque nœud (oui, 80 copies). J'adorerais voir un composant arborescent non hacky qui ne casse aucune règle React.
DanyAlejandro
63

Avertissement

État imbriqué dans React est une mauvaise conception

Lisez cette excellente réponse .

 

Raisonnement derrière cette réponse:

SetState de React est juste une commodité intégrée, mais vous vous rendez vite compte qu'il a ses limites. L'utilisation de propriétés personnalisées et l'utilisation intelligente de forceUpdate vous offrent bien plus. par exemple:

class MyClass extends React.Component {
    myState = someObject
    inputValue = 42
...

MobX, par exemple, l'état des fossés complètement et utilise des propriétés observables personnalisées.
Utilisez les observables au lieu de l'état dans les composants React.

 


la réponse à votre misère - voir l'exemple ici

Il existe un autre moyen plus court de mettre à jour la propriété imbriquée.

this.setState(state => {
  state.nested.flag = false
  state.another.deep.prop = true
  return state
})

Sur une seule ligne

 this.setState(state => (state.nested.flag = false, state))

Remarque: Ceci est l' opérateur Comma ~ MDN , voyez-le en action ici (Sandbox) .

Il est similaire à (bien que cela ne change pas la référence d'état)

this.state.nested.flag = false
this.forceUpdate()

Pour la différence subtile dans ce contexte entre forceUpdateet setStatevoir l'exemple lié.

Bien sûr, cela abuse de certains principes de base, car le statedevrait être en lecture seule, mais puisque vous jetez immédiatement l'ancien état et le remplacez par un nouvel état, c'est tout à fait correct.

Attention

Même si le composant contenant l'état se mettra à jour et se restituera correctement ( sauf ce gotcha ) , les accessoires ne se propageront pas aux enfants (voir le commentaire de Spymaster ci-dessous) . N'utilisez cette technique que si vous savez ce que vous faites.

Par exemple, vous pouvez passer un accessoire plat modifié qui est mis à jour et transmis facilement.

render(
  //some complex render with your nested state
  <ChildComponent complexNestedProp={this.state.nested} pleaseRerender={Math.random()}/>
)

Maintenant, même si la référence à complexNestedProp n'a pas changé ( shouldComponentUpdate )

this.props.complexNestedProp === nextProps.complexNestedProp

le composant se rendra à chaque mise à jour du composant parent, ce qui est le cas après l'appel this.setStateou this.forceUpdatedans le parent.

Effets de la mutation de l'État

L'utilisation de l' état imbriqué et la mutation directe de l'état est dangereuse car différents objets peuvent contenir (intentionnellement ou non) différentes références (plus anciennes) à l' état et ne savent pas nécessairement quand mettre à jour (par exemple lors de l'utilisation PureComponentou si shouldComponentUpdateest implémenté pour revenir false) OU sont destiné à afficher les anciennes données comme dans l'exemple ci-dessous.

Imaginez une chronologie qui est censée rendre des données historiques, la mutation des données sous la main entraînera un comportement inattendu car elle modifiera également les éléments précédents.

flux d'état imbriqué de flux d'état

Quoi qu'il en soit ici, vous pouvez voir Nested PureChildClassqu'il n'a pas été rendu à nouveau en raison de la non-propagation des accessoires.

Qwerty
la source
12
Vous ne devriez modifier aucun état de réaction nulle part. S'il existe des composants imbriqués qui utilisent des données provenant de state.nested ou state.another, ils ne seront pas restitués ni leurs enfants. C'est parce que l'objet n'a pas changé, donc React pense que rien n'a changé.
Zeragamba
@ SpyMaster356 J'ai mis à jour ma réponse pour résoudre ce problème. Notez également que MobX, par exemple, abandonne statecomplètement et utilise des propriétés observables personnalisées . React's setStateest juste une commodité intégrée, mais vous vous rendez vite compte qu'il a ses limites. L'utilisation de propriétés personnalisées et l'utilisation intelligente de forceUpdatevous en donne beaucoup plus. Utilisez les observables au lieu de l'état dans les composants React.
Qwerty
J'ai également ajouté un exemple react-experiments.herokuapp.com/state-flow (lien vers git en haut)
Qwerty
Que se passe-t-il si vous chargez par exemple un objet avec des attributs imbriqués d'une base de données dans l'état? Comment éviter l'état imbriqué dans ce cas?
tobiv
3
@tobiv Il est préférable de transformer les données lors d'une extraction dans une structure différente, mais cela dépend du cas d'utilisation. Il est normal d'avoir un objet imbriqué ou un tableau d'objets dans un état si vous les considérez comme des unités compactes de certaines données. Le problème survient lorsque vous devez stocker des commutateurs d'interface utilisateur ou d'autres données observables (c'est-à-dire des données qui devraient immédiatement changer la vue) car elles doivent être plates afin de déclencher correctement les algorithmes de différence React internes. Pour les changements dans d'autres objets imbriqués, il est facile de déclencher this.forceUpdate()ou d'implémenter des éléments spécifiques shouldComponentUpdate.
Qwerty
21

Si vous utilisez ES2015, vous avez accès à Object.assign. Vous pouvez l'utiliser comme suit pour mettre à jour un objet imbriqué.

this.setState({
  someProperty: Object.assign({}, this.state.someProperty, {flag: false})
});

Vous fusionnez les propriétés mises à jour avec celles existantes et utilisez l'objet renvoyé pour mettre à jour l'état.

Edit: ajout d'un objet vide comme cible à la fonction assign pour vous assurer que l'état n'est pas muté directement comme l'a souligné carkod.

Alyssa Roose
la source
12
Et je peux me tromper, mais je ne pense pas que vous utilisez React sans ES2015.
Madbreaks
16
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);
eyecandy.dev
la source
1
semble la solution la plus élégante ici, 1) éviter la propagation fastidieuse à l'intérieur de setState et 2) mettre bien le nouvel état à l'intérieur de setState, en évitant de muter directement à l'intérieur de setState. Enfin et surtout 3) en évitant d'utiliser une bibliothèque pour une tâche simple
Webwoman
Les deux exemples ne sont pas équivalents. Si le propertycontient plusieurs propriétés imbriquées, la première les supprimera, la seconde non. codesandbox.io/s/nameless-pine-42thu
Qwerty
Qwerty, vous avez raison, merci pour l'effort. J'ai supprimé la version ES6.
eyecandy.dev
Simplicité et simplicité!
Tony Sepia
J'ai utilisé votre solution. Mon obj d'état est petit et cela a bien fonctionné. React restituera-t-il uniquement pour l'état modifié ou rendra-t-il tout?
Kenji Noguchi
14

Il existe de nombreuses bibliothèques pour vous aider. Par exemple, en utilisant immutability-helper :

import update from 'immutability-helper';

const newState = update(this.state, {
  someProperty: {flag: {$set: false}},
};
this.setState(newState);

Utilisation de lodash / fp set:

import {set} from 'lodash/fp';

const newState = set(["someProperty", "flag"], false, this.state);

Utilisation de la fusion lodash / fp :

import {merge} from 'lodash/fp';

const newState = merge(this.state, {
  someProperty: {flag: false},
});
Tokland
la source
Si vous utilisez lodash, vous voudrez probablement essayer de _.cloneDeepdéfinir l'état pour être la copie clonée.
Yixing Liu
@YixingLiu: J'ai mis à jour la réponse à utiliser lodash/fp, nous n'avons donc pas à nous soucier des mutations.
tokland
Un avantage à choisir mergeou à setfonctionner dans lodash / fp en plus de la syntaxe?
Toivo Säwén
Bonne réponse, vous voudrez peut-être ajouter que vous devez également appeler this.setState(newState)les méthodes lodash / fp. Un autre problème est que lodash .set(non-fp) a un ordre d'arguments différent qui m'a fait trébucher pendant un certain temps.
Toivo Säwén
7

Nous utilisons Immer https://github.com/mweststrate/immer pour gérer ces types de problèmes.

Je viens de remplacer ce code dans l'un de nos composants

this.setState(prevState => ({
   ...prevState,
        preferences: {
            ...prevState.preferences,
            [key]: newValue
        }
}));

Avec ça

import produce from 'immer';

this.setState(produce(draft => {
    draft.preferences[key] = newValue;
}));

Avec immer, vous gérez votre état comme un "objet normal". La magie se produit derrière la scène avec des objets proxy.

Joakim Jäderberg
la source
6

Voici une variante de la première réponse donnée dans ce fil de discussion qui ne nécessite pas de packages, bibliothèques ou fonctions spéciales supplémentaires.

state = {
  someProperty: {
    flag: 'string'
  }
}

handleChange = (value) => {
  const newState = {...this.state.someProperty, flag: value}
  this.setState({ someProperty: newState })
}

Afin de définir l'état d'un champ imbriqué spécifique, vous avez défini l'objet entier. Je l' ai fait en créant une variable, newStateet la diffusion du contenu de l'état actuel dans ce premier utilisant le ES2015 opérateur propagation . Ensuite, j'ai remplacé la valeur de this.state.flagpar la nouvelle valeur (puisque j'ai défini flag: value après avoir répandu l'état actuel dans l'objet, le flagchamp dans l'état actuel est remplacé). Ensuite, je règle simplement l'état de somePropertymon newStateobjet.

Matthew Berkompas
la source
6

Bien que l'imbrication ne soit pas vraiment la façon dont vous devez traiter un état de composant, parfois pour quelque chose de facile pour l'imbrication à un niveau.

Pour un État comme celui-ci

state = {
 contact: {
  phone: '888-888-8888',
  email: '[email protected]'
 }
 address: {
  street:''
 },
 occupation: {
 }
}

Une méthode réutilisable que j'utiliserais ressemblerait à ceci.

handleChange = (obj) => e => {
  let x = this.state[obj];
  x[e.target.name] = e.target.value;
  this.setState({ [obj]: x });
};

puis en passant simplement le nom d'obj pour chaque imbrication que vous souhaitez aborder ...

<TextField
 name="street"
 onChange={handleChange('address')}
 />
user2208124
la source
4

J'ai utilisé cette solution.

Si vous avez un état imbriqué comme celui-ci:

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

ici le html avec l'écouteur d'événement

<input type="text" onChange={this.handleChange} " name="friendName" />
Alberto Piras
la source
Confirmé. Cela fonctionne très bien si vous avez déjà affaire à un projet déjà établi avec des propriétés imbriquées.
metal_jacke1
4

Créez une copie de l'état:

let someProperty = JSON.parse(JSON.stringify(this.state.someProperty))

apporter des modifications à cet objet:

someProperty.flag = "false"

maintenant mettre à jour l'état

this.setState({someProperty})
Dhakad
la source
setState est asynchrone. Ainsi, pendant la mise à jour, quelqu'un d'autre peut appeler cette fonction et copier une version obsolète de l'état.
Aviv Ben Shabat
3

Bien que vous ayez posé des questions sur un état du composant React basé sur une classe, le même problème existe avec le hook useState. Pire encore: le hook useState n'accepte pas les mises à jour partielles. Cette question est donc devenue très pertinente lorsque le crochet useState a été introduit.

J'ai décidé de poster la réponse suivante pour m'assurer que la question couvre des scénarios plus modernes où le crochet useState est utilisé:

Si vous avez:

const [state, setState] = useState({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })

vous pouvez définir la propriété imbriquée en clonant le courant et en corrigeant les segments requis des données, par exemple:

setState(current => { ...current, someProperty: { ...current.someProperty, flag: false } });

Ou vous pouvez utiliser la bibliothèque Immer pour simplifier le clonage et l'application de correctifs à l'objet.

Ou vous pouvez utiliser la bibliothèque Hookstate (avertissement: je suis un auteur) pour simplement gérer entièrement les données d'état complexes (locales et globales) et améliorer les performances (lire: ne vous inquiétez pas de l'optimisation du rendu):

import { useStateLink } from '@hookstate/core' 
const state = useStateLink({ someProperty: { flag: true, otherNestedProp: 1 }, otherProp: 2 })

obtenir le champ à rendre:

state.nested.someProperty.nested.flag.get()
// or 
state.get().someProperty.flag

définissez le champ imbriqué:

state.nested.someProperty.nested.flag.set(false)

Voici l'exemple Hookstate, où l'état est profondément / récursivement imbriqué dans une structure de données arborescente .

Andrew
la source
2

Deux autres options non encore mentionnées:

  1. Si vous avez un état profondément imbriqué, demandez-vous si vous pouvez restructurer les objets enfants pour qu'ils reposent à la racine. Cela facilite la mise à jour des données.
  2. Il existe de nombreuses bibliothèques pratiques pour gérer l'état immuable répertoriées dans les documents Redux . Je recommande Immer car il vous permet d'écrire du code de manière mutative mais gère le clonage nécessaire dans les coulisses. Il gèle également l'objet résultant afin que vous ne puissiez pas le muter accidentellement plus tard.
Maison Cory
la source
2

Pour rendre les choses génériques, j'ai travaillé sur les réponses de @ ShubhamKhatri et @ Qwerty.

objet d'état

this.state = {
  name: '',
  grandParent: {
    parent1: {
      child: ''
    },
    parent2: {
      child: ''
    }
  }
};

contrôles d'entrée

<input
  value={this.state.name}
  onChange={this.updateState}
  type="text"
  name="name"
/>
<input
  value={this.state.grandParent.parent1.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent1.child"
/>
<input
  value={this.state.grandParent.parent2.child}
  onChange={this.updateState}
  type="text"
  name="grandParent.parent2.child"
/>

Méthode updateState

setState as @ ShubhamKhatri's answer

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const oldstate = this.state;
  const newstate = { ...oldstate };
  let newStateLevel = newstate;
  let oldStateLevel = oldstate;

  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      newStateLevel[path[i]] = event.target.value;
    } else {
      newStateLevel[path[i]] = { ...oldStateLevel[path[i]] };
      oldStateLevel = oldStateLevel[path[i]];
      newStateLevel = newStateLevel[path[i]];
    }
  }
  this.setState(newstate);
}

setState as @ Qwerty's answer

updateState(event) {
  const path = event.target.name.split('.');
  const depth = path.length;
  const state = { ...this.state };
  let ref = state;
  for (let i = 0; i < depth; i += 1) {
    if (i === depth - 1) {
      ref[path[i]] = event.target.value;
    } else {
      ref = ref[path[i]];
    }
  }
  this.setState(state);
}

Remarque: Ces méthodes ci-dessus ne fonctionneront pas pour les tableaux

Venugopal
la source
2

Je prends très au sérieux les préoccupations déjà exprimées concernant la création d'une copie complète de l'état de votre composant. Cela dit, je suggère fortement Immer .

import produce from 'immer';

<Input
  value={this.state.form.username}
  onChange={e => produce(this.state, s => { s.form.username = e.target.value }) } />

Cela devrait fonctionner React.PureComponent(c'est-à-dire des comparaisons d'états superficiels par React) car il Immerutilise intelligemment un objet proxy pour copier efficacement un arbre d'état arbitrairement profond. Immer est également plus sûr pour les types par rapport aux bibliothèques comme Immutability Helper, et est idéal pour les utilisateurs Javascript et Typescript.


Fonction utilitaire de frappe

function setStateDeep<S>(comp: React.Component<any, S, any>, fn: (s: 
Draft<Readonly<S>>) => any) {
  comp.setState(produce(comp.state, s => { fn(s); }))
}

onChange={e => setStateDeep(this, s => s.form.username = e.target.value)}
Stephen Paul
la source
1
stateUpdate = () => {
    let obj = this.state;
    if(this.props.v12_data.values.email) {
      obj.obj_v12.Customer.EmailAddress = this.props.v12_data.values.email
    }
    this.setState(obj)
}
user3444748
la source
1
Je ne pense pas que ce soit correct, car il objs'agit simplement d'une référence à l'État, donc à travers lui, vous mutez l' this.stateobjet réel
Ciprian Tomoiagă
0

J'ai trouvé que cela fonctionnait pour moi, ayant un formulaire de projet dans mon cas où, par exemple, vous avez un identifiant et un nom et je préfère maintenir l'état d'un projet imbriqué.

return (
  <div>
      <h2>Project Details</h2>
      <form>
        <Input label="ID" group type="number" value={this.state.project.id} onChange={(event) => this.setState({ project: {...this.state.project, id: event.target.value}})} />
        <Input label="Name" group type="text" value={this.state.project.name} onChange={(event) => this.setState({ project: {...this.state.project, name: event.target.value}})} />
      </form> 
  </div>
)

Faites le moi savoir!

Michael Stokes
la source
0

Quelque chose comme ça pourrait suffire,

const isObject = (thing) => {
    if(thing && 
        typeof thing === 'object' &&
        typeof thing !== null
        && !(Array.isArray(thing))
    ){
        return true;
    }
    return false;
}

/*
  Call with an array containing the path to the property you want to access
  And the current component/redux state.

  For example if we want to update `hello` within the following obj
  const obj = {
     somePrimitive:false,
     someNestedObj:{
        hello:1
     }
  }

  we would do :
  //clone the object
  const cloned = clone(['someNestedObj','hello'],obj)
  //Set the new value
  cloned.someNestedObj.hello = 5;

*/
const clone = (arr, state) => {
    let clonedObj = {...state}
    const originalObj = clonedObj;
    arr.forEach(property => {
        if(!(property in clonedObj)){
            throw new Error('State missing property')
        }

        if(isObject(clonedObj[property])){
            clonedObj[property] = {...originalObj[property]};
            clonedObj = clonedObj[property];
        }
    })
    return originalObj;
}

const nestedObj = {
    someProperty:true,
    someNestedObj:{
        someOtherProperty:true
    }
}

const clonedObj = clone(['someProperty'], nestedObj);
console.log(clonedObj === nestedObj) //returns false
console.log(clonedObj.someProperty === nestedObj.someProperty) //returns true
console.log(clonedObj.someNestedObj === nestedObj.someNestedObj) //returns true

console.log()
const clonedObj2 = clone(['someProperty','someNestedObj','someOtherProperty'], nestedObj);
console.log(clonedObj2 === nestedObj) // returns false
console.log(clonedObj2.someNestedObj === nestedObj.someNestedObj) //returns false
//returns true (doesn't attempt to clone because its primitive type)
console.log(clonedObj2.someNestedObj.someOtherProperty === nestedObj.someNestedObj.someOtherProperty) 
Eladian
la source
0

Je sais que c'est une vieille question, mais je voulais quand même partager comment j'ai réussi. En supposant que l'état dans le constructeur ressemble à ceci:

  constructor(props) {
    super(props);

    this.state = {
      loading: false,
      user: {
        email: ""
      },
      organization: {
        name: ""
      }
    };

    this.handleChange = this.handleChange.bind(this);
  }

Ma handleChangefonction est comme ça:

  handleChange(e) {
    const names = e.target.name.split(".");
    const value = e.target.type === "checkbox" ? e.target.checked : e.target.value;
    this.setState((state) => {
      state[names[0]][names[1]] = value;
      return {[names[0]]: state[names[0]]};
    });
  }

Et assurez-vous de nommer les entrées en conséquence:

<input
   type="text"
   name="user.email"
   onChange={this.handleChange}
   value={this.state.user.firstName}
   placeholder="Email Address"
/>

<input
   type="text"
   name="organization.name"
   onChange={this.handleChange}
   value={this.state.organization.name}
   placeholder="Organization Name"
/>
Elnoor
la source
0

Je fais des mises à jour imbriquées avec une recherche réduite :

Exemple:

Les variables imbriquées dans l'état:

state = {
    coords: {
        x: 0,
        y: 0,
        z: 0
    }
}

La fonction:

handleChange = nestedAttr => event => {
  const { target: { value } } = event;
  const attrs = nestedAttr.split('.');

  let stateVar = this.state[attrs[0]];
  if(attrs.length>1)
    attrs.reduce((a,b,index,arr)=>{
      if(index==arr.length-1)
        a[b] = value;
      else if(a[b]!=null)
        return a[b]
      else
        return a;
    },stateVar);
  else
    stateVar = value;

  this.setState({[attrs[0]]: stateVar})
}

Utilisation:

<input
value={this.state.coords.x}
onChange={this.handleTextChange('coords.x')}
/>
Emisael Carrera
la source
0

Ceci est mon état initial

    const initialStateInput = {
        cabeceraFamilia: {
            familia: '',
            direccion: '',
            telefonos: '',
            email: ''
        },
        motivoConsulta: '',
        fechaHora: '',
        corresponsables: [],
    }

Le crochet ou vous pouvez le remplacer par l'état (composant de classe)

const [infoAgendamiento, setInfoAgendamiento] = useState(initialStateInput);

La méthode de handleChange

const actualizarState = e => {
    const nameObjects = e.target.name.split('.');
    const newState = setStateNested(infoAgendamiento, nameObjects, e.target.value);
    setInfoAgendamiento({...newState});
};

Méthode pour définir un état avec des états imbriqués

const setStateNested = (state, nameObjects, value) => {
    let i = 0;
    let operativeState = state;
    if(nameObjects.length > 1){
        for (i = 0; i < nameObjects.length - 1; i++) {
            operativeState = operativeState[nameObjects[i]];
        }
    }
    operativeState[nameObjects[i]] = value;
    return state;
}

Enfin c'est l'entrée que j'utilise

<input type="text" className="form-control" name="cabeceraFamilia.direccion" placeholder="Dirección" defaultValue={infoAgendamiento.cabeceraFamilia.direccion} onChange={actualizarState} />
Ramiro Carbonell Delgado
la source
0

Si vous utilisez formik dans votre projet, il a un moyen simple de gérer ce genre de choses. Voici la façon la plus simple de faire avec formik.

Définissez d'abord vos valeurs initiales dans l'attribut formik initivalues ​​ou dans le react. Etat

Ici, les valeurs initiales sont définies en état réactif

   state = { 
     data: {
        fy: {
            active: "N"
        }
     }
   }

définir ci-dessus initialValues ​​pour le champ formik à l'intérieur de l' initiValuesattribut formik

<Formik
 initialValues={this.state.data}
 onSubmit={(values, actions)=> {...your actions goes here}}
>
{({ isSubmitting }) => (
  <Form>
    <Field type="checkbox" name="fy.active" onChange={(e) => {
      const value = e.target.checked;
      if(value) setFieldValue('fy.active', 'Y')
      else setFieldValue('fy.active', 'N')
    }}/>
  </Form>
)}
</Formik>

Créez une console pour vérifier l'état mis à jour au stringlieu de booleanla setFieldValuefonction formik pour définir l'état ou utilisez l'outil de débogage React pour voir les changements dans les valeurs d'état formik.

Sharad Sharma
la source
0

essayez ce code:

this.setState({ someProperty: {flag: false} });
Soumya
la source
Mais cela supprimera toutes les autres propriétés présentes dans l'objet référencé par somePropertyet après l'exécution du code ci-dessus, seule la flagpropriété restera
Yashas
0

j'ai vu ce qui suit dans un livre:

this.setState(state => state.someProperty.falg = false);

mais je ne sais pas si c'est vrai ..

Dewy Eve
la source