Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactJs: How to wait for componentDidMount() to finish before rendering?

How to wait for async componentDidMount() to finish before rendering?

My 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  
   ...
}

componentDidMount calls this async function:

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

How can I make the render method wait for authUser() in componentDidMount?

like image 217
Stiño Avatar asked Nov 19 '17 11:11

Stiño


People also ask

Does componentDidMount run before render?

componentWillMount() method is the least used lifecycle method and called before any HTML element is rendered. If you want to see then check out the example mentioned above, we just need to add one more method.

Does componentDidMount run after every render?

componentDidMount() only runs once after the first render. componentDidMount() may be called multiple times if the key prop value for the component changes. componentDidMount method is used for handling all network requests and setting up subscriptions during the mounting phase.

How do you wait until Promise is resolved React?

You can use the async/await syntax or call the . then() method on a promise to wait for it to resolve. Inside of functions marked with the async keyword, you can use await to wait for the promises to resolve before continuing to the next line of the function.

How do I make a React to wait render?

To wait for a ReactJS component to finish updating, we use a loading state in our react application by use of the conditional rendering of the component. This can be achieved by the use of the useState and useEffect hooks in the functional components.


3 Answers

Don't wait for componentDidMount to finish before rendering, that would be a misuse of the library, wait for your authUser to finish.

You can do that by utilising your isAuthenticating state property in combination with promises.

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

You could use your existing isAuthenticating flag as follows:

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

Then inside render:

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

This will prevent your component from being added to the DOM until your authUser function completes.

like image 172
linasmnew Avatar answered Oct 10 '22 19:10

linasmnew


Your authUser() function doesn't seem to be set up correctly. You're returning the user object in the callback, but the function itself is not returning anything so var user = authUser(); will always return undefined.

You'll need to change authUser() to either call a callback function or return a Promise that resolves when the user is returned from Firebase. Then set the authentication status to your state once the promise is resolved or the callback is executed. In your render() function return null if the authentication has not yet finished.

Async function with callback:

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

Using the callback with your component:

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
}
like image 32
Kaivosukeltaja Avatar answered Oct 10 '22 21:10

Kaivosukeltaja


componentDidMount will always fire after the first render.

either use componentWillMount or live with the second render, setState triggers a new render and componentWillMount always fires after the component did mount, i.e it rendered correctly.

like image 1
Lucas Reppe Welander Avatar answered Oct 10 '22 21:10

Lucas Reppe Welander