Como atualizar propriedades de estado aninhado no React

390

Estou tentando organizar meu estado usando propriedades aninhadas como esta:

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

Mas atualizando o estado assim,

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

não funciona Como isso pode ser feito corretamente?

Alex Yong
fonte
4
Como assim, não funciona? Esta não é uma pergunta muito boa - o que aconteceu? Algum erro? Que erros?
Darren Sweeney
Tente ler: stackoverflow.com/questions/18933985/…
Andrew Paramoshkin
16
A resposta é Não use o estado aninhado no React . Leia esta excelente resposta.
Qwerty
4
O que deve ser usado em seu lugar?
Jānis Elmeris 17/02/19
42
Simplesmente não usar o estado aninhado é uma resposta inaceitável para o quão amplamente usado o React é hoje. Essa situação vai surgir e os desenvolvedores precisam de uma resposta para isso.
Serraosays

Respostas:

456

Para setStateum objeto aninhado, você pode seguir a abordagem abaixo, pois acho que setState não lida com atualizações aninhadas.

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

A idéia é criar um objeto fictício, executar operações nele e, em seguida, substituir o estado do componente pelo objeto atualizado

Agora, o operador de propagação cria apenas uma cópia aninhada em nível do objeto. Se o seu estado estiver altamente aninhado, como:

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

Você pode definirState usando o operador spread em cada nível, como

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

No entanto, a sintaxe acima fica feia à medida que o estado se torna cada vez mais aninhado e, portanto, recomendo que você use o immutability-helperpacote para atualizar o estado.

Veja esta resposta sobre como atualizar o estado com immutability helper.

Shubham Khatri
fonte
2
@Ohgodwhy, não este não está a aceder ao estado diretamente desde {...} this.state.someProperty retuns um novo obejct para somePropertye estamos modificando que
Shubham Khatri
4
isso não funcionou para mim .. eu estou usando a versão react @ 15.6.1
Rajiv
5
@Stophface Podemos usar Lodash para clone profunda certeza, mas nem todo mundo iria incluir esta biblioteca apenas para setState
Shubham Khatri
15
Isso não viola reactjs.org/docs/… ? Se duas alterações no objeto aninhado forem agrupadas em lotes, a última alteração substituirá a primeira. Portanto, a maneira mais correta seria: this.setState((prevState) => ({nested: {...prevState.nested, propertyToSet: newValue}}) Um pouco tedioso ...
olejorgenb
2
Eu não acho que você precisa de ...prevState,no último exemplo, você só somePropertye seus filhos
Jemar Jones
140

Para escrever em uma linha

this.setState({ someProperty: { ...this.state.someProperty, flag: false} });
Yoseph
fonte
12
É recomendável usar o atualizador para setState em vez de acessar diretamente this.state em setState. reactjs.org/docs/react-component.html#setstate
Raghu Teja
6
Acredito que isso é dizer, não leia this.stateimediatamente depois setStatee espere que ele tenha um novo valor. Contra a chamada this.stateinterna setState. Ou há também um problema com este último que não está claro para mim?
trpt4him
2
Como o trpt4him disse, o link que você deu fala sobre o problema de acessar this.statedepois setState. Além disso, não estamos passando this.statepara setState. O operador espalha propriedades. É o mesmo que Object.assign (). github.com/tc39/proposal-object-rest-spread
Yoseph
3
@RaghuTeja talvez apenas fazendo this.setState(currentState => { someProperty: { ...currentState.someProperty, flag: false} });poderia permitir evitar esse problema?
Webwoman
Eu adicionei a resposta de como obter o mesmo com o gancho useState para garantir a integridade.
Andrew
90

Às vezes, as respostas diretas não são as melhores :)

Versão curta:

esse código

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

deve ser simplificado como algo como

this.state = {
    somePropertyFlag: true
}

Versão longa:

Atualmente, você não deve querer trabalhar com o estado aninhado no React . Como o React não está orientado para trabalhar com estados aninhados e todas as soluções propostas aqui parecem hacks. Eles não usam a estrutura, mas lutam com ela. Eles sugerem escrever um código não tão claro para fins duvidosos de agrupar algumas propriedades. Portanto, são muito interessantes como resposta ao desafio, mas praticamente inúteis.

Vamos imaginar o seguinte estado:

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

O que acontecerá se você alterar apenas um valor de child1? O React não renderiza novamente a exibição porque usa comparação superficial e descobre que a parentpropriedade não foi alterada. Aliás, a mutação direta do objeto de estado é considerada uma prática ruim em geral.

Então você precisa recriar todo o parentobjeto. Mas neste caso, encontraremos outro problema. O React pensará que todas as crianças mudaram seus valores e representará novamente todos eles. Claro que não é bom para o desempenho.

Ainda é possível resolver esse problema escrevendo alguma lógica complicada, shouldComponentUpdate()mas eu preferiria parar por aqui e usar a solução simples da versão curta.

Konstantin Smolyanin
fonte
4
Eu acho que existem casos de uso em que um estado aninhado está perfeitamente bem. Por exemplo, um estado que monitora dinamicamente os pares de valores-chave. Veja aqui como você pode atualizar o estado para apenas uma entrada no estado: reactjs.org/docs/…
pors
11
A comparação rasa é usada para componentes puros por padrão.
Basel Shishani
4
Quero criar um altar em sua homenagem e orar a ele todas as manhãs. Levei três dias para chegar a essa resposta que explica perfeitamente a decisão de projeto OBVIOUS. Todo mundo tenta usar o operador de propagação ou fazer outros hacks apenas porque o estado aninhado parece mais legível por humanos.
Edeph
11
Como você sugere, usar o React para fazer coisas como renderizar uma árvore interativa (por exemplo, minha empresa possui vários sensores em diferentes partes do mundo, cada um de tipo diferente, com propriedades diferentes, em locais diferentes (80+ ) e OF CURSO eles são organizados em uma hierarquia, pois cada implantação custa milhares de dólares e é um projeto completo; portanto, seria impossível gerenciá-los sem uma hierarquia). Estou bastante curioso sobre como você não modelaria coisas como árvores como ... árvores, no React.
DanyAlejandro 4/02/19
10
parece que o React não foi criado para representar qualquer coisa que é armazenada como uma estrutura recursiva de profundidade não trivial. É um pouco decepcionante, pois as visualizações em árvore aparecem em quase todos os aplicativos complexos existentes no mercado (gmail, gerenciador de arquivos, qualquer sistema de gerenciamento de documentos, etc.). Eu usei o React para essas coisas, mas sempre tive um fanático do React dizendo a todos que você está fazendo antipadrões e sugerindo que a solução é manter cópias profundas da árvore no estado de cada nó (sim, 80 cópias). Adoraria ver um componente de exibição em árvore não-hacky que não viole nenhuma regra do React.
DanyAlejandro 5/02/19
63

aviso Legal

Estado aninhado em React é design incorreto

Leia esta excelente resposta .

 

Raciocínio por trás desta resposta:

O setState do React é apenas uma conveniência interna, mas você logo percebe que ele tem seus limites. O uso de propriedades personalizadas e o uso inteligente do forceUpdate oferecem muito mais. por exemplo:

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

O MobX, por exemplo, descarta o estado completamente e usa propriedades observáveis ​​personalizadas.
Use Observables em vez de state nos componentes React.

 


a resposta para sua miséria - veja o exemplo aqui

Há outra maneira mais curta de atualizar qualquer propriedade aninhada.

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

Em uma linha

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

Nota: Este aqui é o operador Vírgula ~ MDN ; veja em ação aqui (Sandbox) .

É semelhante a (embora isso não mude a referência de estado)

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

Para a diferença sutil neste contexto entre forceUpdatee setStateveja o exemplo vinculado.

É claro que isso está abusando de alguns princípios básicos, como o statedeve ser somente leitura, mas, como você está descartando imediatamente o estado antigo e o substituindo por um novo estado, está tudo bem.

Atenção

Mesmo que o componente que contém o estado seja atualizado e renderizado corretamente ( exceto essa opção ) , os objetos não serão propagados para os filhos (veja o comentário do Spymaster abaixo) . Use essa técnica apenas se você souber o que está fazendo.

Por exemplo, você pode aprovar uma proposta simples alterada que é atualizada e aprovada com facilidade.

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

Agora, mesmo que a referência para complexNestedProp não tenha sido alterada ( shouldComponentUpdate )

this.props.complexNestedProp === nextProps.complexNestedProp

o componente será renderizado novamente sempre que o componente pai for atualizado, que é o caso após a chamada this.setStateou this.forceUpdateno pai.

Efeitos da mutação do estado

O uso de estado aninhado e a mutação direta do estado são perigosos porque objetos diferentes podem conter (intencionalmente ou não) referências diferentes (mais antigas) ao estado e podem não necessariamente saber quando atualizar (por exemplo, ao usar PureComponentou se shouldComponentUpdateé implementado para retornar false) OU são destinado a exibir dados antigos, como no exemplo abaixo.

Imagine uma linha do tempo que supostamente renderiza dados históricos. A mutação dos dados nas mãos resultará em um comportamento inesperado, pois também mudará os itens anteriores.

fluxo de estado estado-fluxo-aninhado

De qualquer forma, aqui você pode ver que Nested PureChildClassele não foi renderizado novamente porque os objetos não foram propagados.

Qwerty
fonte
12
Você não deve alterar nenhum estado de reação em lugar algum. Se houver componentes aninhados que usam dados de state.nested ou state.another, eles não serão renderizados novamente nem seus filhos. Isso ocorre porque o objeto não mudou, portanto, o React acha que nada mudou.
Zeragamba
@ SpyMaster356 Atualizei minha resposta para resolver isso. Observe também que o MobX, por exemplo, abandona statecompletamente e usa propriedades observáveis personalizadas . O React's setStateé apenas uma conveniência embutida, mas você logo percebe que ela tem seus limites. O uso de propriedades personalizadas e o uso inteligente de forceUpdateoferecem muito mais. Use Observables em vez de state nos componentes React.
Qwerty
Também acrescentei um exemplo react-experiments.herokuapp.com/state-flow (link para o git no topo)
Qwerty
E se você carregar, por exemplo, um objeto com atributos aninhados de um banco de dados para o estado? Como evitar o estado aninhado nesse caso?
tobiv
3
@tobiv É melhor transformar os dados após uma busca em uma estrutura diferente, mas isso depende do caso de uso. Não há problema em ter um objeto aninhado ou uma matriz de objetos em um estado se você pensar neles como unidades compactas de alguns dados. O problema surge quando você precisa armazenar switches da interface do usuário ou outros dados observáveis ​​(ou seja, dados que devem mudar imediatamente a visualização), pois devem ser planos para acionar corretamente os algoritmos internos do React diffing. Para alterações em outros objetos aninhados, é fácil disparar this.forceUpdate()ou implementar específico shouldComponentUpdate.
Qwerty
21

Se você estiver usando o ES2015, terá acesso ao Object.assign. Você pode usá-lo da seguinte maneira para atualizar um objeto aninhado.

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

Você mescla as propriedades atualizadas com as existentes e usa o objeto retornado para atualizar o estado.

Editar: Adicionado um objeto vazio como destino à função de atribuição para garantir que o estado não seja alterado diretamente, como apontado pelo carkod.

Alyssa Roose
fonte
12
E posso estar errado, mas acho que você não está usando o React sem o ES2015.
Madbreaks
16
const newState = Object.assign({}, this.state);
newState.property.nestedProperty = "new value";
this.setState(newState);
eyecandy.dev
fonte
11
parece a solução mais elegante aqui: 1) evitar a propagação tediosa dentro do setState e 2) colocar bem o novo estado dentro do setState, evitando alterar o diretamente dentro do setState. Last but not least 3) evitando o uso de qualquer biblioteca para apenas uma tarefa simples
Webwoman
Os dois exemplos não são equivalentes. Se o propertycontiver várias propriedades aninhadas, o primeiro as excluirá e o segundo não. codesandbox.io/s/nameless-pine-42thu
Qwerty
Qwerty, você está certo, obrigado pelo esforço. Eu removi a versão ES6.
Eyecandy.dev
Direto e simples!
Tony Sepia
Eu usei sua solução. Meu objetivo estadual é pequeno e funcionou bem. O React renderizará apenas para o estado alterado ou renderizará tudo?
Kenji Noguchi
14

Existem muitas bibliotecas para ajudar com isso. Por exemplo, usando o immutability-helper :

import update from 'immutability-helper';

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

Usando lodash / fp set:

import {set} from 'lodash/fp';

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

Usando lodash / fp merge:

import {merge} from 'lodash/fp';

const newState = merge(this.state, {
  someProperty: {flag: false},
});
tokland
fonte
Se você usar lodash, provavelmente desejará tentar _.cloneDeepdefinir o estado como a cópia clonada.
Yixing Liu
@YixingLiu: Atualizei a resposta para usar lodash/fp, para que não tenhamos que nos preocupar com mutações.
tokland
Alguma vantagem em escolher mergeou setfuncionar no lodash / fp além da sintaxe?
Toivo Säwén 22/01
Boa resposta, talvez você queira acrescentar que também precisa chamar this.setState(newState)os métodos lodash / fp. Outro problema é que o lodash .set(sem FP ) tem uma ordem diferente de argumentos que me atrapalharam por um tempo.
Toivo Säwén 24/01
7

Usamos o Immer https://github.com/mweststrate/immer para lidar com esses tipos de problemas.

Acabei de substituir esse código em um de nossos componentes

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

Com isso

import produce from 'immer';

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

Com o immer, você lida com o seu estado como um "objeto normal". A mágica acontece nos bastidores com objetos proxy.

Joakim Jäderberg
fonte
6

Aqui está uma variação da primeira resposta dada neste tópico, que não requer pacotes extras, bibliotecas ou funções especiais.

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

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

Para definir o estado de um campo aninhado específico, você definiu o objeto inteiro. Fiz isso criando uma variável newStatee espalhando o conteúdo do estado atual primeiro usando o operador de dispersão ES2015 . Em seguida, substituí o valor de this.state.flagcom o novo valor (desde que eu defini flag: value depois que eu espalhei o estado atual no objeto, o flagcampo no estado atual é substituído). Então, simplesmente defino o estado de somePropertypara o meu newStateobjeto.

Matthew Berkompas
fonte
6

Embora o aninhamento não seja realmente como você deve tratar um estado de componente, às vezes é algo fácil para o aninhamento de camada única.

Para um estado como este

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

Um método reutilizável usado seria assim.

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

basta passar o nome do objeto para cada aninhamento que você deseja endereçar ...

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

Eu usei essa solução.

Se você tiver um estado aninhado como este:

   this.state = {
          formInputs:{
            friendName:{
              value:'',
              isValid:false,
              errorMsg:''
            },
            friendEmail:{
              value:'',
              isValid:false,
              errorMsg:''
            }
}

você pode declarar a função handleChange que copia o status atual e o atribui novamente com valores alterados

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

aqui o html com o ouvinte de evento

<input type="text" onChange={this.handleChange} " name="friendName" />
Alberto Piras
fonte
Confirmado. Isso funciona muito bem se você já está lidando com um projeto já estabelecido com propriedades aninhadas.
precisa saber é o seguinte
4

Crie uma cópia do estado:

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

faça alterações neste objeto:

someProperty.flag = "false"

agora atualize o estado

this.setState({someProperty})
Dhakad
fonte
setState é assíncrono. Portanto, enquanto ele é atualizado, alguém pode chamar essa função e copiar uma versão desatualizada do estado.
Aviv Ben Shabat
3

Embora você tenha perguntado sobre um estado do componente React baseado em classe, o mesmo problema existe com o gancho useState. Pior ainda: o gancho useState não aceita atualizações parciais. Portanto, essa pergunta se tornou muito relevante quando o gancho useState foi introduzido.

Decidi postar a seguinte resposta para garantir que a pergunta cubra cenários mais modernos nos quais o gancho useState é usado:

Se você tem:

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

você pode definir a propriedade aninhada clonando a corrente e corrigindo os segmentos necessários dos dados, por exemplo:

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

Ou você pode usar a biblioteca Immer para simplificar a clonagem e aplicação de patches do objeto.

Ou você pode usar a biblioteca Hookstate (exoneração de responsabilidade: eu sou um autor) para simplesmente gerenciar completamente os dados estatais complexos (locais e globais) e melhorar o desempenho (leia-se: para não se preocupar com a otimização da renderização):

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

obtenha o campo para renderizar:

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

defina o campo aninhado:

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

Aqui está o exemplo de Hookstate, onde o estado é aninhado profundamente / recursivamente na estrutura de dados do tipo árvore .

Andrew
fonte
2

Duas outras opções ainda não mencionadas:

  1. Se você tiver um estado profundamente aninhado, considere se é possível reestruturar os objetos filhos para ficarem na raiz. Isso facilita a atualização dos dados.
  2. Existem muitas bibliotecas úteis disponíveis para lidar com o estado imutável listado nos documentos do Redux . Eu recomendo o Immer, pois ele permite que você escreva código de maneira mutante, mas lida com a clonagem necessária nos bastidores. Ele também congela o objeto resultante para que você não possa modificá-lo acidentalmente mais tarde.
Casa Cory
fonte
2

Para tornar as coisas genéricas, trabalhei nas respostas de @ ShubhamKhatri e @ Qwerty.

objeto de estado

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

controles de entrada

<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étodo updateState

setState como resposta de @ ShubhamKhatri

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 como a resposta de @ Qwerty

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

Nota: Esses métodos acima não funcionam para matrizes

Venugopal
fonte
2

Levo muito a sério as preocupações já expressas em torno da criação de uma cópia completa do seu estado componente. Com isso dito, eu sugeriria fortemente Immer .

import produce from 'immer';

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

Isso deve funcionar React.PureComponent(por exemplo, comparações de estado superficial com o React), pois Immerhabilmente usa um objeto proxy para copiar eficientemente uma árvore de estado arbitrariamente profunda. O Immer também é mais tipicamente seguro em comparação com bibliotecas como o Immutability Helper e é ideal para usuários de Javascript e Typescript.


Função de utilitário de texto datilografado

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
fonte
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
fonte
11
Eu não acho que isso é correto, uma vez que objé apenas uma referência para o estado, então por isso que você está transformando o real this.stateobjeto
Ciprian Tomoiagă
0

Descobri que isso funcionava para mim, tendo um formulário de projeto no meu caso em que, por exemplo, você tem um ID e um nome, e prefiro manter o estado de um projeto aninhado.

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

Avise-se me!

Michael Stokes
fonte
0

Algo assim pode ser suficiente,

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
fonte
0

Eu sei que é uma pergunta antiga, mas ainda queria compartilhar como consegui isso. Assumindo que o estado no construtor se parece com isso:

  constructor(props) {
    super(props);

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

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

Minha handleChangefunção é assim:

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

E certifique-se de nomear as entradas de acordo:

<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
fonte
0

Faço atualizações aninhadas com uma pesquisa reduzida :

Exemplo:

As variáveis ​​aninhadas no estado:

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

A função:

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

Usar:

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

Este é o meu estado inicial

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

O gancho ou você pode substituí-lo pelo estado (componente de classe)

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

O método para handleChange

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

Método para definir estado com estados aninhados

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

Finalmente, esta é a entrada que eu uso

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

Se você estiver usando o formik em seu projeto, ele tem uma maneira fácil de lidar com essas coisas. Aqui está a maneira mais fácil de fazer com o formik.

Primeiro defina seus valores iniciais dentro do atributo formik initivalues ​​ou no react. Estado

Aqui, os valores iniciais são definidos no estado de reação

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

define acima valores iniciais para o campo formik dentro do initiValuesatributo 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>

Faça um console para verificar o estado atualizado em stringvez da função booleanformik setFieldValuepara definir o estado ou vá com a ferramenta debugger react para ver as alterações nos valores do estado do formik.

Sharad Sharma
fonte
0

tente este código:

this.setState({ someProperty: {flag: false} });
Soumya
fonte
Mas isso removerá quaisquer outras propriedades presentes no objeto referenciado por somePropertye após a execução do código acima, somente a flagpropriedade permanecerá
Yashas
0

eu vi o seguinte em um livro:

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

mas não tenho certeza se está certo ..

Dewy Eve
fonte