React'ta setState ile bir nesneyi güncelleme


265

Nesnenin özelliklerini ile güncellemek mümkün müdür setState?

Gibi bir şey:

this.state = {
   jasper: { name: 'jasper', age: 28 },
}

Denedim:

this.setState({jasper.name: 'someOtherName'});

ve bu:

this.setState({jasper: {name: 'someothername'}})

Birincisi bir sözdizimi hatasıyla sonuçlanır ve ikincisi hiçbir şey yapmaz. Herhangi bir fikir?


5
ikinci kodu çalışmış olurdu ancak ageiçindeki özelliği kaybetmiş olurdu jasper.
giorgim

Yanıtlar:


577

Devlet güncelleme bir yana Bunu yapmanın birden çok yolu vardır zaman uyumsuz işlem yüzden durum nesnesi güncelleştirmek için, biz kullanımına gerek işlevi güncelleyici ile setState.

1- En basiti:

Önce bir kopyasını oluşturun, jasperardından değişiklikleri yapın:

this.setState(prevState => {
  let jasper = Object.assign({}, prevState.jasper);  // creating copy of state variable jasper
  jasper.name = 'someothername';                     // update the name property, assign a new value                 
  return { jasper };                                 // return new object jasper object
})

Kullanmak yerine Object.assignşu şekilde de yazabiliriz:

let jasper = { ...prevState.jasper };

2- Forma operatörünü kullanma :

this.setState(prevState => ({
    jasper: {                   // object that we want to update
        ...prevState.jasper,    // keep all other key-value pairs
        name: 'something'       // update the value of specific key
    }
}))

Not: Object.assign ve Spread Operatoryalnızca sığ kopya oluşturur , bu nedenle iç içe geçmiş nesne veya nesne dizisi tanımladıysanız, farklı bir yaklaşıma ihtiyacınız vardır.


İç içe durum nesnesini güncelleme:

Durumu şu şekilde tanımladığınızı varsayın:

this.state = {
  food: {
    sandwich: {
      capsicum: true,
      crackers: true,
      mayonnaise: true
    },
    pizza: {
      jalapeno: true,
      extraCheese: false
    }
  }
}

Pizza nesnesinin extraCheese'ini güncellemek için:

this.setState(prevState => ({
  food: {
    ...prevState.food,           // copy all other key-value pairs of food object
    pizza: {                     // specific object of food object
      ...prevState.food.pizza,   // copy all pizza key-value pairs
      extraCheese: true          // update value of specific key
    }
  }
}))

Nesne dizisini güncelleme:

Bir todo uygulamanız olduğunu ve bu formdaki verileri yönettiğinizi varsayalım:

this.state = {
  todoItems: [
    {
      name: 'Learn React Basics',
      status: 'pending'
    }, {
      name: 'Check Codebase',
      status: 'pending'
    }
  ]
}

Herhangi bir todo nesnesinin durumunu güncellemek için, dizi üzerinde bir harita çalıştırın ve her nesnenin benzersiz bir değerini kontrol edin, durumunda condition=true, yeni nesneyi güncellenmiş değerle döndürün, aksi takdirde aynı nesne.

let key = 2;
this.setState(prevState => ({

  todoItems: prevState.todoItems.map(
    el => el.key === key? { ...el, status: 'done' }: el
  )

}))

Öneri: Nesnenin benzersiz bir değeri yoksa dizi dizini kullanın.


Şimdi denediğim gibi çalışıyor ... ikinci yol: S
JohnSnow

7
@JohnSnow Saniyede diğer özellikleri jaspernesneden kaldıracak, console.log(jasper)sadece bir anahtar göreceksiniz name, yaş orada olmayacak :)
Mayank Shukla

1
@JohnSnow, evet bu şekilde uygunsa jasper, objecten iyisi olsun ya da olmasın, başka daha iyi çözümler olabilir :)
Mayank Shukla

6
Burada ne iyi ne Object.assignde yayılan operatör derin kopya özellikleri bahsetmek güzel . Bu şekilde, lodash deepCopyvb. Gibi geçici çözümleri kullanmalısınız .
Alex Vasilev

1
Nasıl olur let { jasper } = this.state?
Brian Le

37

Bu en hızlı ve en okunabilir yoldur:

this.setState({...this.state.jasper, name: 'someothername'});

this.state.jasperZaten bir name özelliği içeriyor olsa bile , yeni ad name: 'someothername'kullanılır.


38
Bu çözümün büyük bir dezavantajı vardır: Durum güncellemelerini optimize etmek için React birden fazla güncellemeyi gruplandırabilir. Sonuç olarak this.state.jasper, mutlaka en son durumu içermez. Gösterimi ile daha iyi kullanın prevState.
günahı

33

Burada spread operatörü ve bazı ES6 kullanın

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

Bu güncelleme jasper ancak diğer özellikler durumdan kaldırılır.
Iaq

11

Bu çözümü kullandım.

Böyle bir yuvalanmış durumunuz varsa:

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

geçerli durumu kopyalayan ve değiştirilen değerlerle yeniden atayan handleChange işlevini bildirebilirsiniz

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

burada olay dinleyicisiyle birlikte html. Durum nesnesine kullanılanla aynı adı kullandığınızdan emin olun (bu durumda 'friendName')

<input type="text" onChange={this.handleChange} " name="friendName" />

Bu benim için çalıştı, ancak bunun yerine kullanmak zorunda kaldım:statusCopy.formInputs[inputName] = inputValue;
MikeyE

9

Burada bir sürü cevap olduğunu biliyorum, ama hiçbirinin setState dışında yeni nesnenin bir kopyasını oluşturmasına şaşırmadım ve sonra sadece setState ({newObject}). Temiz, özlü ve güvenilir. Yani bu durumda:

const jasper = { ...this.state.jasper, name: 'someothername' }
this.setState(() => ({ jasper }))

Veya dinamik bir özellik için (formlar için çok yararlıdır)

const jasper = { ...this.state.jasper, [VarRepresentingPropertyName]: 'new value' }
this.setState(() => ({ jasper }))


7

bunu dene, iyi çalışmalı

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

4
Durum nesnesini doğrudan değiştiriyorsunuz. Bu yaklaşımı kullanmak için kaynak olarak yeni bir nesne ekleyin:Object.assign({}, this.state.jasper, {name:'someOtherName'})
Albizia

3

İlk durum aslında bir sözdizimi hatasıdır.

Bileşeninizin geri kalanını göremediğim için, neden durumunuzdaki nesneleri burada iç içe yerleştirdiğinizi görmek zor. Nesneleri bileşen durumunda yerleştirmek iyi bir fikir değildir. Başlangıç ​​durumunuzu şu şekilde ayarlamayı deneyin:

this.state = {
  name: 'jasper',
  age: 28
}

Bu şekilde, adı güncellemek istiyorsanız, sadece şunu arayabilirsiniz:

this.setState({
  name: 'Sean'
});

Bu amaçladığınız şeyi başaracak mı?

Daha büyük, daha karmaşık veri depoları için Redux gibi bir şey kullanırdım. Ama bu çok daha gelişmiş.

Bileşen durumuyla ilgili genel kural, bileşeni yalnızca bileşenin UI durumunu yönetmek için kullanmaktır (örn. Etkin, zamanlayıcılar, vb.)

Bu referanslara göz atın:


1
Ben sadece bir örnek olarak, nesne iç içe olmalıdır .. Muhtemelen Redux kullanmalıyım ama React temelleri anlamaya çalışıyorum ..
JohnSnow

2
Evet, şimdilik Redux'dan uzak dururum. Temel bilgileri öğrenirken verilerinizi basit tutmaya çalışın. Bu, sizi uyaran garip davalardan kaçınmanıza yardımcı olacaktır. 👍
mccambridge

2

Başka bir seçenek: değişkeninizi Jasper nesnesinden tanımlayın ve sonra sadece bir değişkeni çağırın.

Forma operatörü: ES6

this.state = {  jasper: { name: 'jasper', age: 28 } } 

let foo = "something that needs to be saved into state" 

this.setState(prevState => ({
    jasper: {
        ...jasper.entity,
        foo
    }
})

2

Basit ve dinamik bir yol.

Bu işi yapar, ancak üst öğeyi üst öğeye ayarlamanız gerekir; böylece üst öğe, nesnenin adına işaret eder, id = "jasper" olur ve nesne öğesinin içindeki input öğesinin = özellik adını adlandırır. .

handleChangeObj = ({target: { id , name , value}}) => this.setState({ [id]: { ...this.state[id] , [name]: value } });

Ben ilk cisme katılıyorum, çünkü bu benim durumumdaki nesneler nesnesi için işe yarayan tek şey! Ayrıca verilerimi güncelleme sıkışmış nerede temizlendi. Şimdiye kadar en basit ve dinamik bir şekilde ... Teşekkürler !!
Smit

2

Bunu deneyebilirsiniz : ( Not : giriş etiketinin adı === nesne alanı)

<input name="myField" type="text" 
      value={this.state.myObject.myField} 
     onChange={this.handleChangeInpForm}>
</input>

-----------------------------------------------------------
handleChangeInpForm = (e) => {
   let newObject = this.state.myObject;
   newObject[e.target.name] = e.target.value;
   this.setState({
     myObject: newObject 
   })
}

Stack Overflow'a hoş geldiniz. Bu kod soruyu cevaplayabilirken, bu kodun soruyu neden ve / veya nasıl cevapladığı konusunda ek bağlam sağlamak uzun vadeli değerini arttırır. Cevap nasıl
Elletlar

2

bu derin iç içe nesneler için çok uygun immer immutabe yardımcı programı kullanan başka bir çözümdür ve mutasyon umurumda değil

this.setState(
    produce(draft => {
       draft.jasper.name = 'someothername
    })
)

1

Ayrıca, tüm "durum" nesnesini kopyalamak istemiyorsanız Alberto Piras çözümünü takip edin:

handleChange(el) {
    let inputName = el.target.name;
    let inputValue = el.target.value;

    let jasperCopy = Object.assign({}, this.state.jasper);
    jasperCopy[inputName].name = inputValue;

    this.setState({jasper: jasperCopy});
  }

1

Bunu deneyebilirsiniz:

this.setState(prevState => {
   prevState = JSON.parse(JSON.stringify(this.state.jasper));
   prevState.name = 'someOtherName';
   return {jasper: prevState}
})

veya diğer mülkler için:

this.setState(prevState => {
   prevState = JSON.parse(JSON.stringify(this.state.jasper));
   prevState.age = 'someOtherAge';
   return {jasper: prevState}
})

Veya handleChage işlevini kullanabilirsiniz:

handleChage(event) {
   const {name, value} = event.target;
    this.setState(prevState => {
       prevState = JSON.parse(JSON.stringify(this.state.jasper));
       prevState[name] = value;
       return {jasper: prevState}
    })
}

ve HTML kodu:

<input 
   type={"text"} 
   name={"name"} 
   value={this.state.jasper.name} 
   onChange={this.handleChange}
/>
<br/>
<input 
   type={"text"} 
   name={"age"} 
   value={this.state.jasper.age} 
   onChange={this.handleChange}
/>

Bu JSON.stringify olmadan çalışır .. this.setState (prevState => {prevState = this.state.document; prevState.ValidDate = event.target.value; prevState.ValidDateFormat = dayFormat; return {document: prevState}}); ..Belge nerede devlet türü nesnedir ..
Oleg Borodko

1

Async ve Await kullanmadan Bunu kullanın ...

funCall(){    
     this.setState({...this.state.jasper, name: 'someothername'});
}

Async And Await ile kullanıyorsanız bunu kullanın ...

async funCall(){
      await this.setState({...this.state.jasper, name: 'someothername'});
}

0

Bu kurulum benim için çalıştı:

let newState = this.state.jasper;
newState.name = 'someOtherName';

this.setState({newState: newState});

console.log(this.state.jasper.name); //someOtherName
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.