177 votes

L'utilisation de componentDidMount() asynchrone est-elle bonne ?

Est-ce que l'on utilise componentDidMount() en tant que fonction asynchrone : bonne pratique dans React Native ou dois-je l'éviter ?

J'ai besoin d'obtenir des informations de AsyncStorage lorsque le composant se monte, mais le seul moyen que je connaisse pour rendre cela possible est de faire en sorte que le composant componentDidMount() fonction asynchrone.

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

Cela pose-t-il un problème et existe-t-il d'autres solutions à ce problème ?

2 votes

Les "bonnes pratiques" sont une question d'opinion. Est-ce que cela fonctionne ? Oui.

2 votes

Voici un bon article qui montre pourquoi l'attente asynchrone est une bonne option par rapport aux promesses. hackernoon.com/

0 votes

Utilisez simplement redux-thunk, cela résoudra le problème

189voto

Cù Đức Hiếu Points 3085

Commençons par souligner les différences et déterminer comment cela pourrait causer des problèmes.

Voici le code de l'asynchrone et du "synchrone". componentDidMount() la méthode du cycle de vie :

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

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

En regardant le code, je peux souligner les différences suivantes :

  1. En async mots-clés : En typographie, il s'agit simplement d'un marqueur de code. Il fait 2 choses :
    • Force le type de retour à être Promise<void> au lieu de void . Si vous spécifiez explicitement que le type de retour est non prometteur (ex : void), typescript vous enverra une erreur.
    • Vous permettre d'utiliser await dans la méthode.
  2. Le type de retour est modifié de void a Promise<void>
    • Cela signifie que vous pouvez maintenant le faire :
      async someMethod(): Promise<void> { await componentDidMount(); }
  3. Vous pouvez maintenant utiliser await à l'intérieur de la méthode et de suspendre temporairement son exécution. Comme ceci :

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

Maintenant, comment pourraient-ils causer des problèmes ?

  1. En async Le mot-clé est absolument inoffensif.

  2. Je ne peux pas imaginer une situation dans laquelle vous devez faire un appel à la componentDidMount() de sorte que le type de retour Promise<void> est également inoffensif.

    L'appel à une méthode dont le type de retour est Promise<void> sans await ne fera aucune différence avec l'appel d'un type de retour de type void .

  3. Puisqu'il n'y a pas de méthodes de cycle de vie après componentDidMount() retarder son exécution semble assez sûr. Mais il y a un hic.

    Disons que l'exemple ci-dessus this.setState({users, questions}); serait exécuté après 10 secondes. Au milieu du délai, un autre ...

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

    ... ont été exécutés avec succès et le DOM a été mis à jour. Le résultat est visible pour les utilisateurs. L'horloge a continué à tourner et 10 secondes se sont écoulées. Le retard this.setState(...) s'exécuterait alors et le DOM serait à nouveau mis à jour, cette fois avec les anciens utilisateurs et les anciennes questions. Le résultat serait également visible pour les utilisateurs.

\=> Il est plutôt sûr (je ne suis pas sûr à 100%) d'utiliser async con componentDidMount() méthode. J'en suis un grand fan et jusqu'à présent, je n'ai rencontré aucun problème qui me donne trop de mal de tête.

0 votes

Lorsque vous parlez du problème où un autre setState se produit avant une Promise en attente, n'est-ce pas la même chose avec Promise sans le sucre syntaxique async/await ou même les callbacks classiques ?

3 votes

Oui ! Retarder un setState() comporte toujours un petit risque. Nous devons procéder avec précaution.

0 votes

Je suppose qu'une façon d'éviter les problèmes est d'utiliser quelque chose comme isFetching: true à l'intérieur de l'état d'un composant. Je ne l'ai utilisé qu'avec redux mais je suppose que c'est tout à fait valable avec la gestion de l'état de react-only. Bien que cela ne résout pas vraiment le problème du même état mis à jour quelque part ailleurs dans le code...

24voto

C-F Points 401

Mise à jour en avril 2020 : Le problème semble avoir été corrigé dans la dernière version de React 16.13.1. cet exemple de bac à sable . Merci à @abernier de l'avoir signalé.


J'ai fait quelques recherches, et j'ai trouvé une différence importante : React ne traite pas les erreurs provenant des méthodes asynchrones du cycle de vie.

Donc, si vous écrivez quelque chose comme ça :

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

alors votre erreur sera prise en compte par le limite d'erreur et vous pouvez le traiter et afficher un message de grâce.

Si nous changeons le code comme ceci :

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

ce qui est équivalent à ceci :

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

puis votre erreur sera silencieusement avalée . Honte à toi, React...

Alors, comment traiter les erreurs que ? Le seul moyen semble être une capture explicite comme celle-ci :

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

ou comme ça :

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

Si nous voulons toujours que notre erreur atteigne la limite d'erreur, je peux penser à l'astuce suivante :

  1. Attrapez l'erreur, faites en sorte que le gestionnaire d'erreur change l'état du composant.
  2. Si l'état indique une erreur, lancez-la à partir de la fonction render méthode

Exemple :

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

0 votes

Y a-t-il un problème signalé à ce sujet ? Il pourrait être utile de le signaler si c'est toujours le cas... Merci.

0 votes

@abernier Je pense que c'est par design... Bien que probablement ils pourraient l'améliorer. Je n'ai pas déposé de plainte à ce sujet...

1 votes

Il semble que ce ne soit plus le cas, du moins avec React 16.13.1 tel que testé ici : codesandbox.io/s/bold-ellis-n1cid?file=/src/App.js

11voto

Tiago Alves Points 1612

Votre code est correct et très lisible pour moi. Voir ceci L'article de Dale Jefferson où il montre un asynchrone componentDidMount exemple et a l'air très bien aussi.

Mais certains diront qu'une personne lisant le code peut supposer que React fait quelque chose avec la promesse retournée.

L'interprétation de ce code et la question de savoir s'il s'agit d'une bonne pratique ou non sont donc très personnelles.

Si vous voulez une autre solution, vous pouvez utiliser promesses . Par exemple :

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

3 votes

...ou encore, utilisez simplement une balise en ligne async fonction avec await est à l'intérieur... ?

0 votes

Également une option @ErikAllik :)

0 votes

@ErikAllik auriez-vous un exemple ?

7voto

Lu Tran Points 50

Lorsque vous utilisez componentDidMount sans async mot-clé, le doc dit ceci :

Vous pouvez appeler setState() immédiatement dans componentDidMount(). Cela déclenchera un rendu supplémentaire, mais il aura lieu avant que le navigateur ne mette à jour l'écran.

Si vous utilisez async componentDidMount vous perdrez cette capacité : un autre rendu aura lieu APRÈS que le navigateur ait mis à jour l'écran. Mais, à mon avis, si vous envisagez d'utiliser l'asynchronisme, par exemple pour récupérer des données, vous ne pouvez pas éviter que le navigateur mette à jour l'écran deux fois. Dans un autre monde, il n'est pas possible de PAUSE componentDidMount avant que le navigateur ne mette à jour l'écran.

1 votes

J'aime cette réponse parce qu'elle est concise et étayée par des documents. Pouvez-vous ajouter un lien vers les documents auxquels vous faites référence ?

0 votes

Cela peut même être une bonne chose, par exemple si vous affichez un état de chargement pendant que la ressource est en train de se charger, puis le contenu une fois le chargement terminé.

4voto

dosentmatter Points 138

Je pense que c'est bien tant que vous savez ce que vous faites. Mais ça peut être déroutant parce que async componentDidMount() peut encore fonctionner après componentWillUnmount a été exécuté et le composant a été démonté.

Vous pouvez également vouloir lancer des tâches synchrones et asynchrones à l'intérieur de l'application componentDidMount . Si componentDidMount était asynchrone, il faudrait placer tout le code synchrone avant le premier await . Il peut ne pas être évident pour quelqu'un que le code avant le premier await fonctionne de manière synchrone. Dans ce cas, je garderais probablement componentDidMount synchrone, mais qu'il appelle des méthodes synchrone et asynchrone.

Que vous choisissiez async componentDidMount() vs synchro componentDidMount() en appelant async vous devez vous assurer que vous nettoyez tous les écouteurs ou méthodes asynchrones qui peuvent encore être en cours d'exécution lorsque le composant est démonté.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X