React, durum güncellemelerinin sırasını tutuyor mu?


140

React'in performans optimizasyonu için zaman uyumsuz ve toplu olarak durum güncellemelerini gerçekleştirebileceğini biliyorum. Bu nedenle, aradıktan sonra güncellenecek duruma asla güvenemezsiniz setState. Ama Tepkisiz güvenebilirsiniz aynı sırada devlet güncellemek setStatedenir için

  1. aynı bileşen?
  2. farklı bileşenler?

Aşağıdaki örneklerde düğmeyi tıklamayı düşünün:

1. ihtimali hiç var mı sahte ve b doğrudur için:

class Container extends React.Component {
  constructor(props) {
    super(props);
    this.state = { a: false, b: false };
  }

  render() {
    return <Button onClick={this.handleClick}/>
  }

  handleClick = () => {
    this.setState({ a: true });
    this.setState({ b: true });
  }
}

2. ihtimali hiç var mı sahte ve b doğrudur için:

class SuperContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = { a: false };
  }

  render() {
    return <Container setParentState={this.setState.bind(this)}/>
  }
}

class Container extends React.Component {
  constructor(props) {
    super(props);
    this.state = { b: false };
  }

  render() {
    return <Button onClick={this.handleClick}/>
  }

  handleClick = () => {
    this.props.setParentState({ a: true });
    this.setState({ b: true });
  }
}

Bunların kullanım durumumun aşırı basitleştirmeleri olduğunu unutmayın. Bunu farklı şekilde yapabileceğimin farkındayım, örneğin, örnek 1'de her iki durum parametresini aynı anda güncellemek ve ikinci durum güncellemesini örnek 2'deki birinci durum güncellemesine geri aramada gerçekleştirmek gibi. Ancak, bu benim sorum değil, ve ben sadece React'in bu durum güncellemelerini gerçekleştirmesinin iyi tanımlanmış bir yolu varsa ilgileniyorum, başka bir şey değil.

Belgelerle desteklenen herhangi bir yanıt büyük beğeni topluyor.


1
Şuna

3
bu mantıksız bir soru gibi görünmüyor, bu soruyu tepki sayfasının github konularında da sorabilirsiniz, dan abramov genellikle orada oldukça yardımcı olur. Bu kadar zor sorularım olduğunda sorardım ve o yanıtlardı. Kötü olan, bu tür sorunların resmi belgelerde olduğu gibi kamuya açık olarak paylaşılmamasıdır (böylece başkaları da kolayca erişebilir). Ayrıca React resmi belgelerinin, sorunuzdaki konu gibi bazı konuları kapsamlı şekilde içermediğini hissediyorum.
giorgim

Örneğin şunu ele alalım: github.com/facebook/react/issues/11793 , bu konuda tartışılan şeylerin birçok geliştirici için yararlı olacağına inanıyorum, ancak bu şeyler resmi belgelerde yok, çünkü FB çalışanları bunun gelişmiş olduğunu düşünüyor. Aynısı muhtemelen diğer şeyler için de geçerli. Sorunuzdaki gibi devlet yönetiminin tüm köşe durumlarını inceleyen 'derinlemesine tepki gösteren devlet yönetimi' veya 'devlet yönetiminin tuzakları' gibi bir şey başlıklı resmi bir makalenin fena olmayacağını düşünüyorum. belki FB geliştiricilerini bu tür şeylerle dokümantasyonu genişletmeye zorlayabiliriz :)
giorgim

Bu, sorumdaki ortamla ilgili harika bir makaleye bağlantı. Eyalet kullanım durumlarının% 95'ini kapsamalıdır. :)
Michal

2
@Michal ama o makale bu soruya hala cevap vermiyor IMHO
giorgim

Yanıtlar:


336

React üzerinde çalışıyorum.

TLDR:

Ancak React'in durumu setState ile aynı sırada güncellemesi için güvenebilir misiniz?

  • aynı bileşen?

Evet.

  • farklı bileşenler?

Evet.

Sipariş güncellemeleri her zaman saygı duyulur. "Aralarında" bir ara durum görüp görmediğiniz, bir grup içinde olup olmadığınıza bağlıdır.

Şu anda (React 16 ve öncesi), yalnızca React olay işleyicileri içindeki güncellemeler varsayılan olarak toplu olarak işlenmektedir . İhtiyaç duyduğunuz nadir durumlarda olay işleyicilerinin dışında toplu işlem yapmaya zorlamak için kararsız bir API vardır.

Gelecek sürümlerde (muhtemelen React 17 ve sonrası), React tüm güncellemeleri varsayılan olarak toplu olarak işleyecektir, böylece bunu düşünmek zorunda kalmazsınız. Her zaman olduğu gibi, bu konudaki değişiklikleri React blogunda ve sürüm notlarında duyuracağız .


Bunu anlamanın anahtarı, bir React olay işleyicisinde kaç bileşende kaç tane setState()çağırırsanız çağırın, olay sonunda yalnızca tek bir yeniden işleme üretecekleridir . Çünkü eğer Bu büyük uygulamalarda iyi performans için çok önemlidir Childve Parenther çağrı setState()bir tıklama olayını ele, sen yeniden işlemek istemiyorum Childiki kez.

Her iki örneğinizde de setState()çağrılar bir React olay işleyicisinin içinde gerçekleşir. Bu nedenle, olayın sonunda her zaman birlikte yıkanırlar (ve ara durumu görmezsiniz).

Güncellemeler, oluştukları sıraya göre her zaman yüzeysel olarak birleştirilir . Dolayısıyla, ilk güncelleme ise {a: 10}, ikincisi {b: 20}ve üçüncüsü ise {a: 30}, işlenmiş durum olacaktır {a: 30, b: 20}. Aynı durum anahtarına yapılan daha yeni güncelleme (örneğin abenim örneğimdeki gibi ) her zaman "kazanır".

this.stateNesne ne zaman toplu sonunda UI yeniden işlemek güncellenir. Bu nedenle, durumu önceki bir duruma göre güncellemeniz gerekiyorsa (bir sayacı artırmak gibi), setState(fn)okumak yerine size önceki durumu veren işlevsel sürümü kullanmalısınız this.state. Bunun gerekçesini merak ediyorsanız , bu yorumda derinlemesine anlattım .


Örneğinizde, "ara durumu" görmeyiz çünkü toplu işlemin etkinleştirildiği bir React olay işleyicisinin içindeyiz (çünkü React o olaydan ne zaman çıktığımızı "bilir").

Bununla birlikte, hem React 16 hem de önceki sürümlerde, henüz React olay işleyicilerinin dışında varsayılan olarak gruplama yoktur . Dolayısıyla, örneğinizde bunun yerine bir AJAX yanıt işleyicimiz olsaydı handleClick, her biri setState()olur olmaz işlenirdi. Bu durumda, evet, olur bir ara devlet bkz:

promise.then(() => {
  // We're not in an event handler, so these are flushed separately.
  this.setState({a: true}); // Re-renders with {a: true, b: false }
  this.setState({b: true}); // Re-renders with {a: true, b: true }
  this.props.setParentState(); // Re-renders the parent
});

Bir olay işleyicide olup olmamanıza bağlı olarak davranışın farklı olmasının uygunsuz olduğunun farkındayız . Bu, tüm güncellemeleri varsayılan olarak toplu hale getirecek (ve değişiklikleri eşzamanlı olarak temizlemek için bir katılım API'si sağlayacak) gelecekteki bir React sürümünde değişecektir. Varsayılan davranışı değiştirene kadar (potansiyel olarak React 17'de), gruplamayı zorlamak için kullanabileceğiniz bir API vardır :

promise.then(() => {
  // Forces batching
  ReactDOM.unstable_batchedUpdates(() => {
    this.setState({a: true}); // Doesn't re-render yet
    this.setState({b: true}); // Doesn't re-render yet
    this.props.setParentState(); // Doesn't re-render yet
  });
  // When we exit unstable_batchedUpdates, re-renders once
});

Dahili olarak React olay işleyicilerinin tümü sarmalanmaktadır, unstable_batchedUpdatesbu yüzden varsayılan olarak toplu olarak işlenirler. Bir güncellemeyi unstable_batchedUpdatesiki kez sarmanın bir etkisi olmadığını unutmayın. En dıştaki unstable_batchedUpdatesaramadan çıktığımızda güncellemeler temizlenir .

Bu API, toplu işleme varsayılan olarak zaten etkinleştirildiğinde onu kaldıracağımız anlamında "kararsız" dır. Ancak, onu küçük bir sürümde kaldırmayacağız, bu nedenle React olay işleyicileri dışındaki bazı durumlarda gruplamayı zorlamanız gerekirse, React 17'ye kadar güvenle güvenebilirsiniz.


Özetlemek gerekirse, bu kafa karıştırıcı bir konudur çünkü varsayılan olarak yalnızca olay işleyicileri içindeki react gruplarıdır. Bu, gelecekteki sürümlerde değişecek ve o zaman davranış daha kolay olacaktır. Ancak çözüm, daha az parti yapmak değil , varsayılan olarak daha fazla parti yapmaktır. Yapacağımız şey bu.


1
"Sırayı her zaman doğru yapmanın " bir yolu, geçici bir nesne oluşturmak, farklı değerleri atamak (örneğin obj.a = true; obj.b = true) ve sonunda bunu yapmaktır this.setState(obj). Bu, bir olay işleyicisinin içinde olsanız da olmasanız da güvenlidir. Olay işleyicilerinin dışında sık sık durumu birkaç kez ayarlama hatası yaptığınızı fark ederseniz, düzgün bir numara olabilir.
Chris

Bu nedenle, kümelemenin yalnızca bir olay işleyiciyle sınırlı olmasına güvenemeyiz - sizin de açıkça belirttiğiniz gibi, en azından yakında durum böyle olmayacağı için. O halde, en son duruma erişim sağlamak için güncelleme işlevi ile setState kullanmamız gerekiyor, değil mi? Peki ya bazı verileri okumak ve sonra bunları duruma getirmek için bir XHR yapmak için bazı state.filter kullanmam gerekirse? Görünüşe göre, bir güncelleyiciye ertelenmiş geri arama (ve dolayısıyla bir yan etki) içeren bir XHR koymam gerekecek. Bu bir en iyi uygulama olarak kabul edilir mi?
Maksim Gumerov

1
Ve bu arada bu, bundan hiç okumamamız gerektiği anlamına da geliyor. bazı state.X'i okumanın tek mantıklı yolu, onu argümanından güncelleyici işlevinde okumaktır. Ve this.state'e yazmak da güvenli değildir. Öyleyse neden this.state'e erişime izin verelim? Bunlar bağımsız sorular olabilir ama çoğunlukla açıklamayı doğru yapıp yapmadığımı anlamaya çalışıyorum.
Maksim Gumerov

10
bu cevap reactjs.org belgelerine eklenmelidir
Deen John

2
Bu gönderide "React olay işleyicisi" componentDidUpdateReact 16'dan itibaren diğer yaşam döngüsü geri aramalarını ve diğer yaşam döngüsü geri aramalarını içerip içermediğini açıklar mısınız? Şimdiden teşekkürler!
Ivan

6

Bu aslında oldukça ilginç bir soru ama cevap çok karmaşık olmamalı. Ortama ilişkin cevabı olan harika bir makale var.

1) Bunu yaparsan

this.setState({ a: true });
this.setState({ b: true });

Batching nedeniyle aolacağı trueve bolacağı bir durum olacağını sanmıyorum .false

Bununla birlikte, eğer bbağımlıysa,a o zaman gerçekten beklenen duruma ulaşamayacağınız bir durum olabilir.

// assuming this.state = { value: 0 };
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});

Yukarıdaki tüm çağrılar işleme alındıktan sonra this.state.valuebeklediğiniz gibi 3 değil 1 olacaktır.

Bu makalede bahsedilmektedir: setState accepts a function as its parameter

// assuming this.state = { value: 0 };
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));

Bu bize verecek this.state.value === 3


Ya this.state.value(olay işleyicileri hem güncellenir setState(burada batched edilir) ve AJAX geri aramalar setStateedilir değil batched). Olay işleyicilerinde updater function, işlev tarafından sağlanan güncel güncel durumu kullanarak durumu her zaman güncellediğimden emin olmak için kullanırdım. setStateToplu işlenmediğini bilsem bile AJAX geri araması kodu içinde bir güncelleyici işlevi kullanmalı mıyım ? Lütfen setStatebir AJAX geri araması içindeki kullanımını bir güncelleyici işlevi ile veya kullanmadan netleştirebilir misiniz ? Teşekkür ederim!
tonix

@Michal, merhaba Michal sadece soru sormak istedi, eğer this.setState'e sahipsek ({değer: 0}); this.setState ({değer: this.state.value + 1}); ilk setState yok sayılacak ve sadece ikinci setState çalıştırılacak?
Dickens

@Dickens Her ikisinin setStatede idam edileceğine inanıyorum ama sonuncusu kazanır.
Michal

3

Aynı döngü sırasında birden fazla çağrı gruplanabilir. Örneğin, aynı döngüde bir madde miktarını birden fazla artırmaya çalışırsanız, bu şununla sonuçlanır:

Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)

https://reactjs.org/docs/react-component.html


3

belgede olduğu gibi

setState () , bileşen durumundaki değişiklikleri kuyruğa alır ve React'e bu bileşenin ve alt bileşenlerinin güncellenmiş durumla yeniden oluşturulması gerektiğini söyler. Bu, olay işleyicilerine ve sunucu yanıtlarına yanıt olarak kullanıcı arabirimini güncellemek için kullandığınız birincil yöntemdir.

Değişikliği kuyrukta olduğu gibi önceden gerçekleştirecektir ( FIFO : İlk Giren İlk Çıkar) ilk çağrı, önceden biçimlendiren ilk çağrı olacaktır


merhaba Ali, sadece soru sormak istedim, eğer this.setState ({value: 0}); this.setState ({değer: this.state.value + 1}); ilk setState yok sayılacak ve sadece ikinci setState çalıştırılacak?
Dickens
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.