L'utilisation de componentDidMount()
en tant que fonction asynchrone est-elle une bonne pratique dans React Native ou dois-je l'éviter?
Je dois obtenir des informations sur AsyncStorage
lorsque le composant est monté, mais le seul moyen que je connaisse pour rendre cela possible consiste à rendre la fonction componentDidMount()
async.
async componentDidMount() {
let auth = await this.getAuth();
if (auth)
this.checkAuth(auth);
}
Y at-il un problème avec cela et y at-il d’autres solutions à ce problème?
Votre code est bien et très lisible pour moi. Voir ceci L'article de Dale Jefferson où il montre un exemple asynchrone componentDidMount
et est aussi très beau.
Mais certaines personnes diraient qu'une personne qui lit le code peut supposer que React fait quelque chose avec la promesse retournée.
Donc, l'interprétation de ce code et si c'est une bonne pratique ou non est très personnelle.
Si vous voulez une autre solution, vous pouvez utiliser promesses . Par exemple:
componentDidMount() {
fetch(this.getAuth())
.then(auth => {
if (auth) this.checkAuth(auth)
})
}
En fait, le chargement asynchrone dans ComponentDidMount est un modèle de conception recommandé , car React s'éloigne des méthodes de cycle de vie héritées (composantWillMount, composantWillReceiveProps, composantWillUpdate). et sur Async Rendering.
Ce billet de blog est très utile pour expliquer les raisons pour lesquelles cela est sûr et pour fournir des exemples de chargement asynchrone dans ComponentDidMount:
https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html
Je pense que ça va tant que vous savez ce que vous faites. Mais cela peut être déroutant, car async componentDidMount()
peut toujours être exécuté après que componentWillUnmount
ait été exécuté et que le composant ait été démonté.
Vous voudrez peut-être aussi démarrer des tâches synchrones et asynchrones dans componentDidMount
. Si componentDidMount
était async, vous auriez à mettre tout le code synchrone avant la première await
. Cela pourrait ne pas être évident pour quelqu'un que le code avant la première await
s'exécute de manière synchrone. Dans ce cas, je garderais probablement componentDidMount
synchrone, mais je l'appellerais méthodes de synchronisation et async.
Que vous choisissiez async componentDidMount()
vs sync componentDidMount()
en appelant des méthodes async
, vous devez vous assurer de nettoyer tous les programmes d'écoute ou asynchrones pouvant encore être en cours d'exécution lorsque le composant est démonté.
Mettre à jour:
(Ma version: React 16, Webpack 4, Babel 7):
En utilisant Babel 7, vous découvrirez:
En utilisant ce motif ...
async componentDidMount() {
try {
const res = await fetch(config.discover.url);
const data = await res.json();
console.log(data);
} catch(e) {
console.error(e);
}
}
vous rencontrerez l'erreur suivante ...
Uncaught ReferenceError: regeneratorRuntime n'est pas défini
Dans ce cas, vous devrez installer babel-plugin-transform-runtime} _
https://babeljs.io/docs/en/babel-plugin-transform-runtime.html
Si pour une raison quelconque vous ne souhaitez pas installer le paquet ci-dessus (babel-plugin-transformation-runtime), vous voudrez vous en tenir au modèle Promise ...
componentDidMount() {
fetch(config.discover.url)
.then(res => res.json())
.then(data => {
console.log(data);
})
.catch(err => console.error(err));
}
J'ai fait des recherches et j'ai trouvé une différence importante: React ne traite pas les erreurs des méthodes de cycle de vie async.
Donc, si vous écrivez quelque chose comme ceci:
componentDidMount()
{
throw new Error('I crashed!');
}
alors votre erreur sera interceptée par le error boundry , et vous pourrez la traiter et afficher un message gracieux.
Si nous changeons le code comme ceci:
async componentDidMount()
{
throw new Error('I crashed!');
}
qui est équivalent à ceci:
componentDidMount()
{
return Promise.reject(new Error('I crashed!'));
}
alors votre erreur sera avalée silencieusement . Honte à vous, réagissez ...
Alors, comment traitons-nous les erreurs? Le seul moyen semble être un piège explicite comme ceci:
async componentDidMount()
{
try
{
await myAsyncFunction();
}
catch(error)
{
//...
}
}
ou comme ceci:
componentDidMount()
{
myAsyncFunction()
.catch(()=>
{
//...
});
}
Si nous voulons toujours que notre erreur soit riche en limites d'erreur, je peux penser au truc suivant:
render
.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>;
}
}
Lorsque vous utilisez componentDidMount
sans le mot clé async
, le doc dit ceci:
Vous pouvez appeler setState () immédiatement dans composantDidMount (). Cela déclenchera un rendu supplémentaire, mais cela se produira avant que le navigateur ne mette à jour l'écran.
Si vous utilisez async componentDidMount
, vous perdez cette possibilité: un autre rendu aura lieu APRÈS la mise à jour de l'écran par le navigateur. Mais imo, si vous envisagez d'utiliser asynchrone, telle que la récupération de données, vous ne pouvez pas éviter que le navigateur met à jour l'écran deux fois. Dans un autre monde, il n'est pas possible de mettre en PAUSE le composantDidMount avant que le navigateur ne mette à jour l'écran.