web-dev-qa-db-fra.com

ReactJs: Comment attendre que composantDidMount () se termine avant le rendu?

Comment attendre que async composantDidMount () se termine avant le rendu?

Mon app.jsx:

constructor(props) {
    super(props);

    this.state = {
        loggedInUser: null,
        isAuthenticated: false,
        isAuthenticating: true
    };
}

componentDidMount() {
    try {
        var user = authUser();
        console.log('User: ' + user)
        if (user) {
            console.log('Is logged in: ' + this.state.loggedInUser)
            this.userHasAuthenticated(true);  
        }
    }
    catch(e) {
       alert(e);
    }
    this.setState({ isAuthenticating: false });
}

render() { 
   console.log('in render: ' + this.state.loggedInUser)
   // Should execute **after** authUser() in componentDidMount has finished  
   ...
}

composantDidMount appelle cette fonction asynchrone:

function authUser() {
    firebase.auth().onAuthStateChanged(function(user) {
        return user
    })
}
console.log('in render: ' + this.state.loggedInUser)

Comment faire en sorte que la méthode de rendu attende authUser () dans le composantDidMount? 

6
Stiño

N'attendez pas que componentDidMount se termine avant de générer le rendu, cela constituerait une mauvaise utilisation de la bibliothèque, attendez que votre authUser soit terminé.

Vous pouvez le faire en utilisant votre propriété d'état isAuthenticating en combinaison avec promises.

function authUser() {
   return new Promise(function (resolve, reject) {
      firebase.auth().onAuthStateChanged(function(user) {
         if (user) {
            resolve(user);
         } else {
            reject('User not logged in');
         }             
      });
   });
}

Vous pouvez utiliser votre indicateur isAuthenticating existant comme suit:

componentDidMount() {
    authUser().then((user) => {
       this.userHasAuthenticated(true);
       this.setState({ isAuthenticating: false });
    }, (error) => {
       this.setState({ isAuthenticating: false });
       alert(e);
    });
}

Puis à l’intérieur du rendu:

render() {
   if (this.state.isAuthenticating) return null;
   ...
}

Cela empêchera votre composant d'être ajouté au DOM jusqu'à ce que votre fonction authUser soit terminée.

9
linasmnew

Si vous souhaitez que le composant ne soit pas rendu, enveloppez votre composant avec un composant d'autorisation personnalisé et ne le restez pas si l'utilisateur n'est pas connecté . Il est déconseillé d'empêcher l'appel de la fonction render.

0
ShacharW

Je pense que le problème est que authUser est asynchrone. Je voudrais utiliser des promesses afin de traiter de manière propre la réponse async. Je ne connais pas l'API de Firebase, mais apparemment, le support proposé a été promis: https://firebase.google.com/docs/functions/terminate-functions

Sinon, vous pouvez utiliser une bibliothèque telle que bluebird et modifier votre fonction authUser pour renvoyer une promesse: http://bluebirdjs.com/docs/working-with-callbacks.html

Si vous n'êtes pas familier avec les promesses, vous devez tout d'abord vous informer sur les principes fondamentaux: https://bitsofco.de/javascript-promises-101/

0
Davide Ungari

Votre fonction authUser() ne semble pas être configurée correctement. Vous renvoyez l'objet utilisateur dans le rappel, mais la fonction elle-même ne renvoie rien, donc var user = authUser(); renverra toujours undefined.

Vous devrez modifier authUser() pour appeler une fonction de rappel ou renvoyer une Promise qui sera résolue lorsque l'utilisateur sera renvoyé de Firebase. Définissez ensuite le statut d'authentification sur votre état une fois la promesse résolue ou le rappel exécuté. Dans votre fonction render(), renvoyez null si l'authentification n'est pas encore terminée.

Fonction asynchrone avec rappel:

function authUser(callback) {
    firebase.auth().onAuthStateChanged(function(user) {
        callback(user);
    })
}

Utilisation du rappel avec votre composant:

componentDidMount() {
    try {
        authUser(function(user) {
            console.log('User: ' + user)
            if (user) {
                console.log('Is logged in: ' + this.state.loggedInUser)
                this.userHasAuthenticated(true);  
                this.setState({ isAuthenticating: false });
            }
        });
    }
    catch(e) {
       alert(e);
    }
}

render() { 
   console.log('in render: ' + this.state.loggedInUser)
   if (this.state.isAuthenticating === true) {
       return null;
   }
   // Rest of component rendering here
}
0
Kaivosukeltaja

componentDidMount tirera toujours après le premier rendu.

soit utiliser componentWillMount soit vivre avec le deuxième rendu, setState déclenche un nouveau rendu et composantWillMount est toujours déclenché après le montage du composant, c’est-à-dire qu’il est restitué correctement.

0