Eşzamansız componentDidMount () kullanmak iyi mi?


139

componentDidMount()React Native'de zaman uyumsuz işlev olarak kullanmak iyi bir uygulama mı yoksa bundan kaçınmalı mıyım?

AsyncStorageBileşen bağlandığında biraz bilgi almam gerekiyor , ancak bunu mümkün componentDidMount()kılmanın tek yolu işlevi eşzamansız hale getirmek .

async componentDidMount() {
    let auth = await this.getAuth();
    if (auth) 
        this.checkAuth(auth);
}

Bununla ilgili herhangi bir sorun var mı ve bu soruna başka çözümler var mı?


2
"İyi uygulama" bir fikir meselesidir. Çalışıyor mu? Evet.
Kraylog

2
İşte async await'in
Shubham

sadece redux-thunk kullanın, sorunu çözecek
Tilak Maddy

@TilakMaddy Neden her react uygulamasının redux kullandığını düşünüyorsunuz?
Mirakurun

@Mirakurun Neden tüm yığın taşması, gün içinde düz javascript soruları sorarken jQuery kullandığımı varsaydı?
Tilak Maddy

Yanıtlar:


162

Farklılıklara işaret ederek ve bunun nasıl sorunlara neden olabileceğini belirleyerek başlayalım.

Eşzamansız ve "eşzamanlı" componentDidMount()yaşam döngüsü yönteminin kodu :

// This is typescript code
componentDidMount(): void { /* do something */ }

async componentDidMount(): Promise<void> {
    /* do something */
    /* You can use "await" here */
}

Koda bakarak aşağıdaki farklara işaret edebilirim:

  1. asyncAnahtar kelimeler: daktilo versiyonunda, bu sadece bir kod belirtecidir. 2 şey yapar:
    • Dönüş türünü Promise<void>yerine olmaya zorlayın void. İade türünü açıkça taahhüt edilmeyen olarak belirtirseniz (örn: void), typcript size bir hata verecektir.
    • awaitYöntem içinde anahtar kelimeleri kullanmanıza izin verin .
  2. Dönüş türü değiştirilir voidiçinPromise<void>
    • Şimdi bunu yapabileceğiniz anlamına gelir:
      async someMethod(): Promise<void> { await componentDidMount(); }
  3. Artık awaityöntem içinde anahtar sözcüğü kullanabilir ve yürütmesini geçici olarak duraklatabilirsiniz. Bunun gibi:

    async componentDidMount(): Promise<void> {
        const users = await axios.get<string>("http://localhost:9001/users");
        const questions = await axios.get<string>("http://localhost:9001/questions");
    
        // Sleep for 10 seconds
        await new Promise(resolve => { setTimeout(resolve, 10000); });
    
        // This line of code will be executed after 10+ seconds
        this.setState({users, questions});
        return Promise.resolve();
    }

Şimdi, nasıl sorun çıkarabilirler?

  1. asyncAnahtar kelime kesinlikle zararsızdır.
  2. componentDidMount()Yönteme bir çağrı yapmanız gereken herhangi bir durumu hayal edemiyorum, böylece dönüş türü Promise<void>de zararsızdır.

    Bir yöntem olan geri dönüş tipine çağrılması Promise<void>olmadan awaitanahtar kelimenin dönüş türüne sahip birini arayarak hiçbir fark yaratacak void.

  3. componentDidMount()Yürütmeyi geciktirdikten sonra yaşam döngüsü yöntemi olmadığından oldukça güvenli görünüyor. Ama bir sorun var.

    Diyelim ki, yukarıdakiler this.setState({users, questions});10 saniye sonra yürütülecektir. Gecikme süresinin ortasında, başka bir ...

    this.setState({users: newerUsers, questions: newerQuestions});

    ... başarıyla yürütüldü ve DOM güncellendi. Sonuç kullanıcılar tarafından görüldü. Saat ilerlemeye devam etti ve 10 saniye geçti. Gecikme this.setState(...)daha sonra yürütülür ve DOM o kez eski kullanıcılar ve eski sorularla tekrar güncellenir. Sonuç, kullanıcılar tarafından da görülebilir.

=> Kullanımı (ben emin% 100 üzereyim) oldukça güvenlidir asyncile componentDidMount()yöntemle. Bunun büyük bir hayranıyım ve şimdiye kadar bana çok fazla baş ağrısı veren herhangi bir sorunla karşılaşmadım.


Bekleyen bir Promise'den önce başka bir setState'in meydana geldiği sorun hakkında konuştuğunuzda, bu async / await sözdizimsel şeker ve hatta klasik geri çağırmalar olmadan Promise ile aynı değil mi?
Clafou

3
Evet! A'yı geciktirmek setState()her zaman küçük bir risk taşır. Dikkatli ilerlemeliyiz.
Cù Đức Hiếu

Sanırım sorunlardan kaçınmanın bir yolu, isFetching: truebir bileşenin durumu gibi bir şey kullanmaktır . Bunu sadece redux ile kullandım ama sanırım sadece tepki durum yönetimi için tamamen geçerli. Aynı durumun kodun başka bir yerinde güncellenmesi sorununu gerçekten
çözmese de

1
Buna katılıyorum. Aslında, isFetchingbayrak çözümü, özellikle arka uç yanıtını beklerken ön uçta bazı animasyonları oynatmak istediğimizde oldukça yaygındır ( isFetching: true).
Cù Đức Hiếu

3
Bileşen kaldırıldıktan sonra setState yaparsanız sorunlarla karşılaşabilirsiniz
Eliezer Steinbock

18

Nisan 2020 Güncellemesi: Sorun en son React 16.13.1'de düzeltilmiş gibi görünüyor, bu sanal alan örneğine bakın . @ Abernier'e bunu işaret ettiği için teşekkürler.


Biraz araştırma yaptım ve önemli bir fark buldum: React, eşzamansız yaşam döngüsü yöntemlerinden gelen hataları işlemez.

Öyleyse, böyle bir şey yazarsanız:

componentDidMount()
{
    throw new Error('I crashed!');
}

daha sonra, hatanız hata sınırı tarafından yakalanır ve bunu işleyebilir ve zarif bir mesaj görüntüleyebilirsiniz.

Kodu şu şekilde değiştirirsek:

async componentDidMount()
{
    throw new Error('I crashed!');
}

buna eşdeğer olan:

componentDidMount()
{
    return Promise.reject(new Error('I crashed!'));
}

o zaman hatanız sessizce yutulacak . Yazıklar olsun sana React ...

Öyleyse, hataları daha sonra nasıl işleriz? Tek yol böyle açık bir şekilde yakalamak gibi görünüyor:

async componentDidMount()
{
    try
    {
         await myAsyncFunction();
    }
    catch(error)
    {
        //...
    }
}

veya bunun gibi:

componentDidMount()
{
    myAsyncFunction()
    .catch(()=>
    {
        //...
    });
}

Hala hatamızın hata sınırına ulaşmasını istiyorsak, şu numarayı düşünebilirim:

  1. Hatayı yakalayın, hata işleyicinin bileşen durumunu değiştirmesini sağlayın
  2. Durum bir hata gösteriyorsa, bunu renderyöntemden atın

Misal:

class BuggyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { error: null };
  }

  buggyAsyncfunction(){ return Promise.reject(new Error('I crashed async!'));}

  async componentDidMount() {
    try
    {
      await this.buggyAsyncfunction();
    }
    catch(error)
    {
        this.setState({error: error});
    }
  }

  render() {
    if(this.state.error)
        throw this.state.error;

    return <h1>I am OK</h1>;
  }
}

bunun için rapor edilmiş bir sorun var mı? Durum hala devam ediyorsa bunu bildirmek faydalı olabilir ... thx
abernier

@abernier Ben tenezzül olduğunu düşünüyorum ... Muhtemelen iyileştirebilirlerdi. Bununla ilgili herhangi bir sorun
CF

1
artık durum öyle görünmüyor, en azından burada test edildiği gibi React 16.13.1 ile: codesandbox.io/s/bold-ellis-n1cid?file=/src/App.js
abernier

9

Kodunuz iyi ve bana çok okunabilir. Bu bak Dale Jefferson'ın makalesine o bir zaman uyumsuz gösterir componentDidMountsıra gerçekten iyi bir örnek ve görünüyor.

Ancak bazı insanlar, kodu okuyan bir kişinin React'in geri dönen sözle bir şeyler yaptığını varsayabileceğini söyler.

Dolayısıyla bu kodun yorumlanması ve iyi bir uygulama olup olmadığı çok kişiseldir.

Başka bir çözüm istiyorsanız, sözler kullanabilirsiniz . Örneğin:

componentDidMount() {
    fetch(this.getAuth())
      .then(auth => {
          if (auth) this.checkAuth(auth)
      })
}

3
... veya ayrıca, içinde s asyncile bir satır içi işlevi kullanın await...?
Erik Kaplun

@ErikAllik :)
Tiago Alves

@ErikAllik bir örneğiniz var mı?
Pablo Rincon

1
@PabloRincon (async () => { const data = await fetch('foo'); const result = await submitRequest({data}); console.log(result) })()nerede fetchve submitRequestsözlerini geri döndüren işlevlerdir.
Erik Kaplun

Bu kod kesinlikle kötüdür çünkü getAuth işlevinde meydana gelen herhangi bir hatayı yutacaktır. Ve işlev ağda bir şey yaparsa (örneğin), hatalar beklenmelidir.
CF

6

Anahtar kelime componentDidMountolmadan kullandığınızda async, doküman şunu söyler:

SetState () öğesini hemen componentDidMount () içinde çağırabilirsiniz. Ekstra bir görüntülemeyi tetikleyecek, ancak tarayıcı ekranı güncellemeden önce gerçekleşecek.

Eğer kullanırsanız async componentDidMount, bu yeteneği kaybedersiniz: Tarayıcı ekranı güncelledikten sonra başka bir render olacaktır. Ama imo, zaman uyumsuz kullanmayı düşünüyorsanız, veri getirme gibi, tarayıcının ekranı iki kez güncellemesini engelleyemezsiniz. Başka bir dünyada, tarayıcı ekranı güncellemeden önce componentDidMount'u DURAKLATMAK mümkün değildir


1
Bu yanıtı beğendim çünkü kısa ve dokümanlar tarafından destekleniyor. Lütfen referans verdiğiniz dokümanlara bir bağlantı ekler misiniz?
theUtherSide

Bu, örneğin kaynak yüklenirken bir yükleme durumunu ve tamamlandığında içeriği görüntülüyorsanız bile iyi bir şey olabilir.
Hjulle

3

Güncelleme:

(Yapım: React 16, Webpack 4, Babel 7):

Babel 7'yi kullanırken şunları keşfedeceksiniz:

Bu kalıbı kullanarak ...

async componentDidMount() {
    try {
        const res = await fetch(config.discover.url);
        const data = await res.json();
        console.log(data);
    } catch(e) {
        console.error(e);
    }
}

aşağıdaki hatayla karşılaşacaksınız ...

Yakalanmamış ReferenceError: regeneratorRuntime tanımlı değil

Bu durumda babel-plugin-transform-runtime kurmanız gerekecek

https://babeljs.io/docs/en/babel-plugin-transform-runtime.html

Herhangi bir nedenle yukarıdaki paketi (babel-plugin-transform-runtime) kurmak istemiyorsanız, Promise modeline bağlı kalmak isteyeceksiniz ...

componentDidMount() {
    fetch(config.discover.url)
    .then(res => res.json())
    .then(data => {
        console.log(data);
    })
    .catch(err => console.error(err));
}

3

Bence ne yaptığını bildiğin sürece sorun değil. Ancak bu kafa karıştırıcı async componentDidMount()olabilir, çünkü componentWillUnmountçalıştıktan ve bileşen kaldırıldıktan sonra hala çalışıyor olabilir .

Ayrıca içeride hem eşzamanlı hem de eşzamansız görevleri başlatmak isteyebilirsiniz componentDidMount. Eğer componentDidMountzaman uyumsuz oldu, önce önce tüm senkron kod koymak zorunda kalacak await. İlkinden önceki kodun awaiteşzamanlı olarak çalıştığı birisi için açık olmayabilir . Bu durumda, muhtemelen componentDidMounteşzamanlı kalırdım, ancak eşitleme ve eşzamansız yöntemleri çağırırdım.

İster async componentDidMount()senkronizasyon componentDidMount()çağırma asyncyöntemlerini seçin, ister bileşen ayrıldığında hala çalışan dinleyicileri veya zaman uyumsuz yöntemleri temizlediğinizden emin olmalısınız.


2

React, eski yaşam döngüsü yöntemlerinden (componentWillMount, componentWillReceiveProps, componentWillUpdate) uzaklaştığı ve Async Rendering'e geçtiği için , aslında, ComponentDidMount'a zaman uyumsuz yükleme, önerilen bir tasarım modelidir .

Bu blog gönderisi, bunun neden güvenli olduğunu açıklamada ve ComponentDidMount'ta eşzamansız yükleme için örnekler sağlamada çok yararlıdır:

https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html


3
Zaman uyumsuz oluşturmanın aslında yaşam döngüsünü açıkça eşzamansız yapmakla hiçbir ilgisi yoktur. Aslında bir anti-modeldir. Önerilen çözüm, aslında bir yaşam döngüsü yönteminden eşzamansız bir yöntemi çağırmaktır
Clayton Ray

1

Bunun gibi bir şey kullanmayı seviyorum

componentDidMount(){
   const result = makeResquest()
}
async makeRequest(){
   const res = await fetch(url);
   const data = await res.json();
   return data
}
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.