Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

componentWillMount is called before render()? #7048

Closed
mnbucher opened this issue Jun 15, 2016 · 7 comments
Closed

componentWillMount is called before render()? #7048

mnbucher opened this issue Jun 15, 2016 · 7 comments
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@mnbucher
Copy link

mnbucher commented Jun 15, 2016

Hi together

Learning React at the moment and not sure if I'm not unterstanding a core part of React or this is a bug...

My idea was to create a simple login and don't show the login window if the user is already logged in. Authentication is handled via Firebase, so here's a small snippet of my code:

var Login = React.createClass ({

getInitialState: function(){
    return { loggedIn: false };
},

componentWillMount: function() {

    [ ... some firebase code ... ]

    firebase.auth().onAuthStateChanged(function(user) {
        if (user) {
            this.setState({ loggedIn: true });
        }
        else {
            this.setState({ loggedIn: false });
        }

}

render: function() {
    return (
      <div>
      {this.state.loggedIn ? (
        <div>
        <Backend/>
        </div>
      ) : (
         [ ... login form.... ]
      ) }
      </div>
    );
  }
}); // END LOGIN

So my problem is the following: componentWillMount should be called before render, so if the user is already logged in, the new state is loggedIn: true and so it only renders the backend and not the login form... but unfortunately this new state in componentWillMount is not taken before render and it actually renders the login form

Thanks for the answer, cheers!

Martin

@gaearon
Copy link
Collaborator

gaearon commented Jun 15, 2016

Does onAuthStateChanged invoke the callback synchronously?

@gaearon gaearon added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Jun 15, 2016
@mnbucher
Copy link
Author

How can I control that?

@aweary
Copy link
Contributor

aweary commented Jun 15, 2016

You can try and render a <Loading /> component until firebase has resolved the user for the first time.

var Login = React.createClass ({

getInitialState: function(){
    return { 
      loggedIn: false,
      loaded: false
    };
},

componentWillMount: function() {

    [ ... some firebase code ... ]

    firebase.auth().onAuthStateChanged(function(user) {
        if (user) {
            this.setState({ loggedIn: true, loaded: true });
        }
        else {
            this.setState({ loggedIn: false, loaded: true });
        }

}

render: function() {
    if (!this.state.loaded) return <Loading />;
    return (
      <div>
      {this.state.loggedIn ? (
        <div>
        <Backend/>
        </div>
      ) : (
         [ ... login form.... ]
      ) }
      </div>
    );
  }
}); // END LOGIN

That way you don't show either screen until you've resolved the actual auth state for the user. Otherwise, you can't really control the firebase API to that extent, it's likely inherently asynchronous as it has to make an HTTP request before it resolves, which is almost always going to occur after your component renders.

@jimfb
Copy link
Contributor

jimfb commented Jun 15, 2016

Yeah, sounds like your firebase.auth.onAuthStateChanged(...) callback doesn't get called immediately (they likely launch a request to the firebase server and set the auth state when the server replies). Regardless, the data is not available at render time.

This is effectively a request for #6481 and #1739. Closing as a duplicate of those two issues.

The workaround suggested by @aweary is currently what @sebmarkbage generally recommends.

@jimfb jimfb closed this as completed Jun 15, 2016
@mnbucher
Copy link
Author

Hmm, seems like code below this.sate({}) is completely ignored in my example… what could cause that?
Sorry for interrupting you guys… I'm completely new to React. Next time I'm posting first in StackOverflow… :)

@jimfb
Copy link
Contributor

jimfb commented Jun 15, 2016

@mnbucher setState shouldn't be completely ignored, but it may be asynchronous. There is a big red notice in the documentation (https://facebook.github.io/react/docs/component-api.html#setstate) that says:

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

Maybe that's what you're running into?

@ibkernel
Copy link

ibkernel commented Aug 2, 2016

I have encountered the same problem, you should try this solution, it solved the problem for me.

componentWillMount: function() {

    [ ... some firebase code ... ]
    var _this = this
    firebase.auth().onAuthStateChanged(function(user) {
        if (user) {
            _this.setState({ loggedIn: true });
        }
        else {
            _this.setState({ loggedIn: false });
        }

}

ahoarfrost added a commit to MetaSeek-Sequencing-Data-Discovery/metaseek that referenced this issue Mar 15, 2017
selector fields in filter bar now have selections based on keys in
corresponding summaryData object; fixed issue with render happening
before WillMount that breaks everything, render Loading.js until
summaryData is ready (facebook/react#7048)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug
Projects
None yet
Development

No branches or pull requests

5 participants