1

I'm following an authentication tutorial, and I'm trying to understand this:

componentDidMount() {
    this.props.firebase.auth.onAuthStateChanged(authUser => {
      authUser
        ? this.setState({ authUser })
        : this.setState({ authUser: null });
    });
  }

If componentDidMount() is called after Component rendering, why a function that evaluates if the user is logged in should be called after the renderization?

I think that the result of this is:

  1. React renders the unathenticated page
  2. After that, it evaluates if the user is authenticated
  3. Finally renders the authenticated page

Instead of:

  1. Evaluate if the user is authenticated
  2. Render the authenticated page

Can you say me in what part of the cycle I'm wrong?

I understand that componentDidMount() is used for don't block the flow of the page rendering, but if a user have a session previously, and enters again to the website, he can't access to the private page until the public page is rendered and the onAuthStateChanged() is called.

Angel Luis
  • 487
  • 2
  • 5
  • 19
  • Does this answer your question? [React render() is being called before componentDidMount()](https://stackoverflow.com/questions/45337165/react-render-is-being-called-before-componentdidmount) – SuleymanSah Dec 18 '19 at 11:21
  • 2
    You are wrong in part "Evaluate if the user is authenticated" is a single step. It actually two steps (send request / receive response): 1. Send request to evaluate (here you render loader) 2. Receive ok response (here you render authenticated page) 3. Receive error response (here you redirect to login page) – Arseniy-II Dec 18 '19 at 11:27
  • 1
    If you considered componentWillMount for that use case [read react docs about why you shouldn't do it](https://reactjs.org/docs/react-component.html#unsafe_componentwillmount). – Arseniy-II Dec 18 '19 at 11:29
  • I understand that this method is necessary for don't block the rendering, but I believe that an authentication state evaluation for the first time should be before the first rendering. Because always the initial state will be null, and if the user preserves the session, will load before the unauthenticated page, that I think will be slower than wait to the function that evaluates if the user is logged in. – Angel Luis Dec 18 '19 at 11:33
  • 1
    @AngelLuis I'm not sure I understood you. How can you load smth before your page? There are quite a few methods to authenticate. Please describe how you want your auth process. – Arseniy-II Dec 18 '19 at 11:38
  • @Arseniy-II first I get if the user is authenticated and then I render the component that shows the private page. Like a typical server rendering. With `componentWillMount()` I can reach that behaviour, but it's assumed that I can't use that method. – Angel Luis Dec 18 '19 at 11:41
  • @AngelLuis what user will see before he authenticated? It is not instant process it requires some time. – Arseniy-II Dec 18 '19 at 11:42
  • @Arseniy-II For example the user signs in to the page with Firebase authentication. He closes the browser. The session is still active, but when he opens again the website, the App constructor will set the `authUser` as null. But the user is authenticated, and the first thing that he will see, is the unauthenticated page until `componentDidMount()` is called. – Angel Luis Dec 18 '19 at 11:45
  • @AngelLuis you can add condition and display authenticated page with loader if user has session. or unauthenticated page with loader if user don't has session. And after response from server you can either leave user at authenticated/unauthenticated page or redirect him – Arseniy-II Dec 18 '19 at 11:50
  • 1
    @Arseniy-II so do you think that the better approach is, for example: App state (`authUser: null, firstLoad: true`), put a Loading animation while it evaluates if the user is logged in, update the state, redirect depending on the state, and finally update the state `firstLoad` to false, to avoid the Loading animation during the rest of the state session. – Angel Luis Dec 18 '19 at 11:55
  • Yes. It is very common approach – Arseniy-II Dec 18 '19 at 11:57
  • Thank you! I'll try to write a self answer. – Angel Luis Dec 18 '19 at 11:58

2 Answers2

0

Having in count that the use of methods like componentWillMount() is totally discouraged, one of the best approachs to handle this is:

  1. Create a state to evaluate if the React component is rendered for the first time: this.state = { firstTime: true }
  2. Renderize a loading animation component if this.state.firsTime is true
  3. After that, componentDidMount() will be called, and here will be called the function that evaluates if is signed in
  4. If the user is signed in, or not, will update this.state.authUser with the result, and change this.state.firstLoad to false
  5. In the next render, as this.state.firstTime is false, won't renderize the loading component. This time will check the result of this.state.authUser and render the corresponding component
Angel Luis
  • 487
  • 2
  • 5
  • 19
0

React docs said to do api calls in componentDidMount because you can setState here and see them in render.

You need not to track firstTime.

componentDidMount(){
  if(!this.state.auth){
    ..makecall.then(authUser =>{
        this.setState({authUser})
        localStorage.setState("authUser",authUser)
     })
  }
}
render(
 if(!this.state.authUser){
  return <div>Loading</div>
 }
 return(
   return <div>
   ...
   ... 
   </div>
 )
)

You can also use Private routing. Once you fetched your authUser token store it in localstorage and

    const PrivateRoute = ({ component: Component, ...rest }) => (
      if(localstorage.getItem("authUser")) {
        return <Route {...rest} render={(props) => (
             <Component authUser={localstorage.getItem("authUser")} {...props} />
          }
           return <Redirect to={{
              pathname: '/login',
              state: { from: props.location }
            }} />
      )}
    )
  ..
  .. 
   export default function App(){
     return (
        <Router>
         <Route path="/login" component={Login}/>
         <PrivateRoute path='/protected' component={Protected} />
        </Router>
}

You need to verify the user always when they visit any route so this is simple approach. It will check auth in one place and if not authenticated then it redirect to login page.
If for more security / Good code purpose you will need to do validation of token too. That you can do in PrivateRoute function or in componentDidMount

Rajan Lagah
  • 2,373
  • 1
  • 24
  • 40