Tepki: setState kullanarak state.item [1] durumunda nasıl güncellenir?


260

Kullanıcının kendi formunu tasarlayabileceği bir uygulama oluşturuyorum. Örneğin, alanın adını ve hangi sütunların dahil edilmesi gerektiğini belirtin.

Bileşen burada JSFiddle olarak kullanılabilir .

İlk durumum şöyle:

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

Kullanıcı değerlerden herhangi birini değiştirdiğinde durumu güncellemek istiyorum, ancak doğru nesneyi hedeflemek için zor zamanlar yaşıyorum:

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

this.setStateGüncellemesini sağlamak için nasıl zanaat yapmalıyım items[1].name?



1
bu soru için seçtiğiniz cevap nedir?
Braian Mellor

Yanıtlar:


128

Bu iş parçacığında çok fazla yanlış bilgi olduğundan, yardımcı kütüphaneler olmadan nasıl yapabileceğiniz aşağıda açıklanmıştır:

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

İsterseniz 2. ve 3. adımları birleştirebilirsiniz:

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

Veya her şeyi tek bir satırda yapabilirsiniz:

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

Not: itemsBir dizi yaptım . OP bir nesne kullandı. Ancak, kavramlar aynıdır.


Terminalinizde / konsolunuzda neler olduğunu görebilirsiniz:

 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 evet, yardımcı yöntemler kesinlikle güzel, ama bence herkes kaputun altında neler olduğunu bilmeli :-)
mpen

2. ve 3. adımlara neden ihtiyacımız var? Neden ilk adımdan sonra klonu doğrudan değiştiremiyoruz?
Evmorov

1
@Evmorov Çünkü derin bir klon değil. Adım 1, içindeki nesneleri değil, diziyi klonlamaktır. Başka bir deyişle, yeni dizinin içindeki nesnelerin her biri hala bellekteki mevcut nesneleri "işaret eder" - birini değiştirmek diğerini değiştirir (bir ve aynıdır). Ayrıca aşağıdaki items[0] === clone[0]terminal örneğimdeki bite bakın . Üçlü =, nesnelerin aynı şeyi gösterip göstermediğini kontrol eder.
mpen

Dizideki öğenin dizinini bilmiyorsanız daha karmaşık hale gelir.
Ian Warburton

@IanWarburton Pek değil. items.findIndex()bunun kısa işini yapmalı.
mpen

126

Bunun için updatedeğişmezlik yardımcısını kullanabilirsiniz :

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

Ya da shouldComponentUpdate()kullanarak bir yaşam döngüsü yönteminde ===bu öğedeki değişiklikleri tespit etmeyi umursamıyorsanız , durumu doğrudan düzenleyebilir ve bileşeni yeniden oluşturmaya zorlayabilirsiniz - bu, @limelights'ın yanıtıyla aynıdır. bir nesneyi durumdan çıkarma ve düzenleme.

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

Düzenleme sonrası ekleme:

Check out Basit Bileşen İletişim dan ders tepki-eğitim bir durum değişikliği tetiklemek için gereken bir çocuk bileşenine bir devlet tutan ebeveynden bir geri çağırma işlevi nasıl geçirileceği bir örnek için.


92

Yanlış yol!

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

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

Yorumlarda çok daha iyi geliştiricilerin işaret ettiği gibi: devleti mutasyona uğratmak yanlıştır!

Bunu çözmem için biraz zamanımı aldı. Yukarıda çalışır ama Tepki'nin gücünü alır. Örneğin componentDidUpdate, doğrudan değiştirildiği için bunu bir güncelleme olarak görmez.

Yani doğru yol :

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

25
ES6 "const" kullandığınız için mi?
nkkollaw

49
items[1].role = e.target.valueMutasyona uğrayan durumu doğrudan çağırmıyor musunuz?
antony

28
devleti mutasyona uğratıyorsunuz, bu tamamen önerilen tepki gibi değişmez devleti koruma fikrine aykırıdır. Bu, büyük bir uygulamada size çok fazla acı verebilir.
ncubica

8
@MarvinVK, cevabınız "Bu yüzden en iyi uygulama:" ve ardından "this.forceUpdate ();" setState () tarafından üzerine yazılabileceği için önerilmez, bkz. facebook.github.io/react/docs/react-component.html#state . Bunu değiştirmek en iyisidir, böylece gelecekteki okuyuculara kafa karıştırmaz.
James Z.

8
Sadece yorumların birçoğunun düzenlemeler nedeniyle ilgisiz olduğunu ve doğru yolun aslında doğru yol olduğunu belirtmek istedim .
heez

50

Vanilya JavaScript'in: tipik olarak üç yöntem kullanılır durumuna, tepki derin iç içe nesneler / değişkenleri değiştirmek için Object.assign, değişmezlik-yardımcı ve cloneDeepgelen Lodash .

Bunu başarmak için daha az popüler üçüncü taraf kütüphaneleri de var, ancak bu cevapta sadece bu üç seçeneği ele alacağım. Ayrıca, dizi yayma gibi bazı ek vanilya JavaScript yöntemleri de mevcuttur (örneğin @ mpen'in cevabına bakın), ancak çok sezgisel değil, kullanımı kolay ve tüm durum manipülasyon durumlarını idare edebilir.

As üst sivri sayısız kez yazarlar devletin doğrudan mutasyon teklif cevapları, açıklama olarak vardı: sadece bunu yapmaz . Bu, kaçınılmaz olarak istenmeyen sonuçlara yol açacak olan her yerde bulunan bir Tepki anti-paternidir. Doğru yolu öğrenin.

Yaygın olarak kullanılan üç yöntemi karşılaştıralım.

Bu durum nesne yapısı göz önüne alındığında:

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

innerEyaletin geri kalanını etkilemeden en içteki alanın değerini güncellemek için aşağıdaki yöntemleri kullanabilirsiniz .

1. Vanilya JavaScript'in Object.assign

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>

O, unutmayın Object.assign derin klonlama gerçekleştirmez , çünkü sadece kopyalar gayrimenkul değerleri ve en o yüzden ona bir denir ne kopyalayarak sığ (yorumlar).

Bunun çalışması için sadece ilkel tiplerin ( outer.inner), yani dizelerin, sayıların, booleanların özelliklerini manipüle etmeliyiz .

Bu örnekte, yeni bir sabit (oluştururken const newOuter...kullanarak) Object.assign(boş bir nesne oluşturur ki, {}), kopyalar outer(nesne { inner: 'initial value' }içine) ve daha sonra kopya farklı bir nesne { inner: 'updated value' } üzerinde o.

Bu şekilde, yeni oluşturulan newOutersabit { inner: 'updated value' }, innermülkün geçersiz kılınmasından bu yana bir değer tutacaktır . Bu newOuter, durumdaki nesneyle bağlantılı olmayan yepyeni bir nesnedir, bu nedenle gerektiğinde mutasyona uğrayabilir ve durum, güncelleme komutu verilinceye kadar aynı kalır ve değişmez.

Son bölüm, durumdaki setOuter()orijinali outeryeni oluşturulan bir newOuternesneyle değiştirmek için ayarlayıcıyı kullanmaktır (yalnızca değer değişecektir, özellik adı değişmeyecektir outer).

Şimdi daha derin bir durumumuz olduğunu hayal edin state = { outer: { inner: { innerMost: 'initial value' } } }. newOuterNesneyi oluşturmaya ve durumun outeriçeriğiyle doldurmaya Object.assignçalışabiliriz , ancak çok derin iç içe olduğundan , innerMostdeğerini bu yeni oluşturulan newOuternesneye kopyalayamayacağız innerMost.

Hala kopya olabilir inneryukarıdaki örnekte olduğu gibi, ama şimdi bir nesne ve beri değil ilkel, referans gelen newOuter.innerkopyalanır outer.inneryerel ile sona erecek bunun yerine, hangi vasıta newOuterdoğrudan devlet nesneye bağlı nesne .

Bu, yerel olarak oluşturulan mutasyonların nesneyi (devlette) newOuter.innerdoğrudan etkileyeceği anlamına gelir outer.inner, çünkü bunlar aslında aynı şey haline gelmiştir (bilgisayar belleğinde).

Object.assign bu nedenle sadece ilkel tipte değerler tutan en içteki üyelere sahip nispeten basit bir seviye derin durum yapınız varsa işe yarayacaktır.

Güncellemeniz gereken daha derin nesneleriniz (2. seviye veya daha fazla) varsa kullanmayın Object.assign. Durumu doğrudan mutasyona uğratma riskiniz vardır.

2. Lodash'ın klonu

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>

Lodash'ın cloneDeep'inin kullanımı çok daha kolaydır. Derin bir klonlama yapar , bu nedenle içeride çok seviyeli nesneler veya dizilerle oldukça karmaşık bir duruma sahipseniz, sağlam bir seçenektir. Sadece cloneDeep()üst düzey durum özelliği, klonlanan parçayı istediğiniz şekilde değiştirin ve setOuter()tekrar duruma getirin.

3. değişmezlik yardımcısı

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-helperyepyeni bir seviyeye alır ve bu konuda serin şey olabilir sadece olmasıdır $setdevlet öğelere değerleri, aynı zamanda $push, $splice, $merge(vs) onları. İşte mevcut komutların bir listesi .

Yan notlar

Yine, derinden iç içe geçmiş ( ) değil, durum nesnesinin ( bu örneklerde) setOuteryalnızca birinci düzey özelliklerini değiştirdiğini unutmayın . Farklı bir şekilde davransaydı, bu soru olmazdı.outerouter.inner

Hangisi projeniz için doğru ?

Harici bağımlılıklar istemiyorsanız veya kullanamıyorsanız ve basit bir durum yapısına sahipseniz , sadık kalın Object.assign.

Büyük ve / veya karmaşık bir durumu manipüle ederseniz , Lodash's cloneDeepakıllıca bir seçimdir.

Gelişmiş yeteneklere ihtiyacınız varsa , yani devlet yapınız karmaşıksa ve üzerinde her türlü işlemi yapmanız gerekiyorsa, immutability-helperdurum manipülasyonu için kullanılabilen çok gelişmiş bir araçtır.

... yoksa bunu gerçekten yapmak zorunda mısın ?

React'ın durumunda karmaşık bir veri tutarsanız, belki de bu veriyi ele almanın diğer yollarını düşünmek için iyi bir zamandır. React bileşenlerinde karmaşık bir durum nesnesini doğru ayarlamak basit bir işlem değildir ve farklı yaklaşımları düşünmenizi şiddetle tavsiye ederim.

Büyük olasılıkla karmaşık verilerinizi bir Redux mağazasında tutmanız, redüktörler ve / veya sagalar kullanarak orada ayarlamanız ve seçiciler kullanarak erişmeniz daha iyi olur.


Object.assignderin bir kopya yapmaz. Diyelim ki eğer a = {c: {d: 1}}ve b = Object.assign({}, a)sonra idam edersiniz b.c.d = 4, o a.c.dzaman mutasyona uğrar.
Awol

Haklısın, en 1içteki nesnenin ( a.c.d) değeri mutasyona uğrayacak. Ancak b, aşağıdaki gibi birinci düzey halefini yeniden atayacaksanız, b.c = {f: 1}ilgili kısmı amutasyona uğramayacak (kalacak {d: 1}). Yine de güzel yakala, cevabı hemen güncelleyeceğim.
Nörotransmitter

Tanımladığınız şey aslında a shallow copydeğil a deep copy. Ne shallow copyanlama geldiğini karıştırmak kolaydır . İçinde shallow copy, a !== bancak kaynak nesneden her anahtar için a,a[key] === b[key]
Awol

Evet, Object.assigncevabın sığlıklarından açıkça bahsedildi .
Nörotransmitter

JSON.parse(JSON.stringify(object))ayrıca derin klon için bir varyanttır. Performans kötü cloneDeepolsa da kötü . measurethat.net/Benchmarks/Show/2751/0/...
tylik

35

Ben de aynı problemi yaşadım. İşte işe yarayan basit bir çözüm!

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

11
@TranslucentCloud - bu kesinlikle doğrudan bir mutasyon değildir. orijinal dizi klonlandı, değiştirildi ve daha sonra durum, klonlanmış Array kullanılarak tekrar ayarlandı.
vsync

@vsync evet, şimdi orijinal cevabı düzenledikten sonra bu hiç bir mutasyon değil.
Nörotransmitter

2
@TranslucentCloud - Ayrıca, düzenlememin onunla hiçbir ilgisi yok, tutum için çok teşekkürler. @Jonas, cevabında sadece düzelttiğim {köşeli parantezler yerine kıvırcık parantez kullanarak basit bir hata yaptı
vsync

31

SetState üzerindeki React belgelerine göre , Object.assignburada diğer cevapların önerdiği şekilde kullanmak ideal değildir. setStateEşzamansız davranışının doğası gereği, bu tekniği kullanan sonraki çağrılar, önceki çağrıları geçersiz kılarak istenmeyen sonuçlara neden olabilir.

Bunun yerine, React dokümanları setState, önceki durumda çalışan güncelleyici formunun kullanılmasını önerir . Bir dizi veya nesneyi güncellerken yeni bir dizi veya nesne döndürmeniz gerektiğini unutmayın, çünkü React durum değişmezliğini korumamızı gerektirir. Bir diziyi sığ kopyalamak için ES6 sözdiziminin yayma işlecini kullanmak, dizinin belirli bir dizininde bir nesnenin bir özelliğini oluşturmak veya güncelleştirmek şöyle görünür:

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

2
ES6 kullanıyorsanız bu uygun cevaptır. @Jonas'ın yanıtladığı satır içi. Ancak açıklama nedeniyle öne çıkıyor.
Sourabh

Evet bu kod mükemmel çalışıyor ve çok basit bir kod ..
Shoeb Mirza

29

Önce istediğiniz öğeyi alın, o nesnede ne istediğinizi değiştirin ve tekrar durumuna getirin. Durumu yalnızca bir nesneyi geçirerek getInitialStatekullanma şekliniz, anahtarlı bir nesne kullanırsanız çok daha kolay olurdu.

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

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

3
Hayır, verim veriyor Uncaught TypeError: Cannot read property 'items' of null.
martins

Hayır olmaz. Hatanız büyük olasılıkla yaptığınız işten kaynaklanıyor getInitialState.
Henrik Andersson

10
@HenrikAndersson Örneğinizde bir şeyler doğru görünmüyor. itemshiçbir yerde tanımlanmamış.
Edward D'Souza

1
@ EdwardD'Souza Kesinlikle haklısın! Cevabım bunun nasıl tanımlanması ve kullanılması gerektiğini göstermektir. Askers kodunun kurulum şekli istediği şey için çalışmaz, bu yüzden anahtarlı bir nesneye ihtiyaç duyulur.
Henrik Andersson

7
Bu tipik bir React önleme kalıbıdır, itemdevletin kendi this.state.items[1]değişkenine bir referans atarsınız. Sonra , item( item.name = 'newName') modifiye edersiniz ve böylece durumu doğrudan mutasyona uğratırsınız, ki bu çok cesaret kırılmıştır. Örneğinizde, aramaya bile gerek yoktur this.setState({items: items}), çünkü durum zaten doğrudan mutasyona uğramıştır.
Nörotransmitter

22

Durumu yerinde değiştirmeyin. Beklenmedik sonuçlara neden olabilir. Dersimi öğrendim! Her zaman bir kopya / klon ile çalışın, iyi bir kopyadır Object.assign():

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
Örneğinde ne var items? Bunu mu demek istediniz this.state.items?
Buh Buh

Bunu test ettim ama bir satırı eksik. Yukarıda items[1] = item;bir satır olmalı items = this.state.items;. Benim javascript paslı dikkat ve bu benim iyi ya da kötü olup olmadığını hiçbir fikrim yok bu yüzden ev projem için tepki öğreniyorum :-)
Greg0ry

4

Gerçekten çok basit.

İlk olarak tüm nesne nesnesini durumdan çekin, items nesnesinin bir kısmını istediğiniz gibi güncelleyin ve tüm nesne nesnesini setState aracılığıyla tekrar durumuna getirin.

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 "Nesne.assign, ilkel tipte değerlere sahip en içteki üyelere sahip nispeten basit bir seviye derin durum yapınız varsa çalışır".
Nörotransmitter

3

Yukarıdaki seçeneklerin hiçbiri benim için ideal olmadığından haritayı kullandım:

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

2

Mutasyon içermez:

// 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
Nerede newItemsayarlandığını göremiyorum .
Nörotransmiter

Bir harita artı dizideki karşılaştırma performans için korkunç değil mi?
Natassia Tavares

@NatassiaTavares .. ne? belki kafanız karıştı fo ofveya forEachharita en hızlı.
Deano

1

Bunu şaşırtıcı derecede zor buldum ve ES6 yayılma büyüsünün hiçbiri beklendiği gibi çalışmadı. Düzenleme amacıyla işlenen öğe özelliklerini almak için böyle bir yapı kullanıyordum.

kullanarak bulundu updateimmutability-helperbu basitleştirilmiş örnekte en basit olanı yöntemi :

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

uyarlandığı gibi https://github.com/kolodny/immutability-helper#computed-property-names adresinden

güncellenecek dizi üyesi daha iç içe bir karmaşık nesnedir derinlemesine uygun derin kopyalama yöntemini kullanın dayalı .

Düzen parametrelerini işlemenin kesinlikle daha iyi yolları vardır, ancak bu dizilerin nasıl ele alınacağıyla ilgilidir. Her bir alt öğe için ilgili değerler de bunların dışında hesaplanabilir, ancak containerState'i aşağı aktarmayı daha uygun buldum, böylece childs istedikleri özellikleri getirebilir ve verilen dizinde üst durum dizisini güncelleyebilirler.

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

Tek satırda ok işlevli dizi eşlemesi kullanma

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

1
Bu temel olarak bir yıl önce yayınlanan diğer cevap ile aynı
CertainPerformance


0

İşlev tutamacı değişikliğini hareket ettirir ve bir dizin parametresi eklerdim

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

Dinamik form bileşenine gidin ve PopulateAtCheckboxes bileşenine prop olarak iletin. Öğelerinizin üzerinden geçerken, aşağıda gösterildiği gibi tutamaç değişikliğine iletilecek ek bir sayaç (aşağıdaki kodda dizin olarak adlandırılır) ekleyebilirsiniz

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

Son olarak değişiklik dinleyicinizi aşağıda gösterildiği gibi arayabilirsiniz

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

React'in durumunu asla doğrudan değiştirmeyin.
Nörotransmitter

0

Öğesinin yalnızca bir bölümünü değiştirmeniz gerekiyorsa Array, durumu olarak ayarlanmış bir tepki bileşeniniz var.

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

Bu güncellemek için en iyisi red-onede Arrayşöyle:

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)

nedir newArray? demek istiyorsun newItems? Bunu yaparsanız, daha sonra sadece bir eşya ile devletten ayrılmaz mı?
micnil

Bu newItems, statenesneye yeni bir özellik getirecek ve varolan itemsözelliği güncelleştirmeyecektir .
Nörotransmiter

0

veya dinamik olarak oluşturulmuş bir listeniz varsa ve dizini bilmiyorsanız, yalnızca anahtarınız veya kimliğiniz varsa:

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

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

    ItemsCopy.push(entry)
})


this.setState({Items:ItemsCopy});

0

Kodu deneyin:

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

this.setState({items: cloneObj });

0

Aşağıdaki kod parçası donuk beynimde kolay gitti. Nesneyi kaldırma ve güncellenen nesneyle değiştirme

    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

Başka bir bileşen oluşturmaya (diziye girmesi gereken nesne için) ve aşağıdakileri sahne olarak geçirmeye ne dersiniz?

  1. bileşen dizini - dizinde dizi oluşturmak / güncellemek için dizin kullanılır.
  2. set function - Bu işlev, verileri bileşen dizinine göre diziye koyar.
<SubObjectForm setData={this.setSubObjectData}                                                            objectIndex={index}/>

Burada {index}, bu SubObjectForm öğesinin kullanıldığı konuma göre iletilebilir.

ve setSubObjectData böyle bir şey olabilir.

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

SubObjectForm'da, bu.props.setData veri değişikliğinde aşağıda verildiği gibi çağrılabilir.

<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
bu çok uygun değil, sadece ikinci öğeyi güncellemek için tüm diziyi yineliyor musunuz?
wscourge

Ayrıca, ilk dizideki öğeyi de mutasyona item = Object.assign({}, item, {name: 'newName'});
uğratıyorsunuz

0

@ JonnyBuchanan'ın cevabı mükemmel çalışıyor, ancak sadece dizi durumu değişkeni için. Durum değişkeninin tek bir sözlük olması durumunda, bunu izleyin:

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

Sen değiştirebilirsiniz [input]senin sözlüğün alan adıyla ve e.target.valuedeğeri ile. Bu kod, formumun giriş değişikliği olayında güncelleme işini gerçekleştirir.


-3

Bunu deneyin kesinlikle işe yarayacak, diğer durumda denedim ama işe yaramadı

import _ from 'lodash';

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

React'in durumunu asla doğrudan değiştirmeyin.
Nörotransmitter

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

ve Metin kutusundan değiştir add onChange olayı

onChange = {
   (event) => {                                                
      this.handleChanges(event.target.value, "title");
   }
}
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.