Search code examples
reactjsaws-amplify

Rendering React components according to Amplify logged in user


How can I render different React components based on the output of Amplify.currentAuthenticatedUser() (or similar equivalent) ?

The following works -

render() {
    if (Auth.user) {
        return (
        <LoggedInContainer pages={this.state.pages} />
        );
    } else {
        return (
        <LoginForm loginHandler={this.login} />
        );
    };
}

but if I refresh the page the session seems to be lost and I have to re- login, ie I think referencing Auth.user is sub- optimal.

If I try the following -

render() {
    Auth.currentAuthenticatedUser().then(user => {
        return (
        <LoggedInContainer pages={this.state.pages} />
        );
    }).catch(e => {
        return (
        <LoginForm loginHandler={this.login} />
        );
    });
}

then I get Your render method should have return statement react/require-render-return

If I try the following -

getCurrentUser() {
    Auth.currentAuthenticatedUser().then(user => {
        console.log(user.attributes.email);
        return user.attributes.email;
    }).catch(e => {
        return undefined;
    });
}
render() {
    if (this.getCurrentUser()) {
        return (
        <LoggedInContainer pages={this.state.pages} />
        );
    } else {
        return (
        <LoginForm loginHandler={this.login} />
        );
    };
}

then I never get logged in because the output of getCurrentUser is always undefined, despite the fact that the console.log() line in getCurrentUser prints out the correct email of the user!

Is there no simple way to get the value of the current Amplify logged- in user, and render different React components according to its value ?


Solution

  • I like to use a switch statement in combination with an enum when I'm dealing with rendering different components based on state/value. The switch deals with rendering the correct component and the enum values are used to give values to the cases of the switch statement.

    What happens in this component:

    1. The component uses 2 state values: authStatus and user, both set to the default value.

    2. ComponentDidMount() gets launched after the initial render and getCurrentUser() is launched here.

    3. In getCurrentUser() we set the state depending on the outcome of this function. This state will be used for rendering the correct component.

    4. In render()the switch statement will be launched and we use our authStatus state value for rendering the correct component.

    Note: You are dealing with an async request, when none of the cases in the switch are matched it will render the default case which in my case will render null, this is something you would want to avoid and instead atleast render a load icon or give the user some feedback to what is happening. I created another enum called ProgressStatus that can help you with this, you would use this enum the same way in a switch statement as the one below.

    import React, { Component } from 'react';
    
    export enum AuthStatus {
      isLoggedIn = "isLoggedIn",
      isNotLoggedIn = "isNotLoggedIn",
      Uninitialized = "uninitialized"
    }
    
    export enum ProgressStatus {
      InProgress = "InProgress",
      Uninitialized = "Uninitialized",
      Done = "Done",
      Error = "Error"
    }
    
    class AuthenticationShell extends Component {
    
      state = {
        authStatus: AuthStatus.Uninitialized,
        user: undefined
      }
    
      componentDidMount = () => {
        // this function will launch when the component is mounted to the dom
        this.getCurrentUser()
      }
    
      getCurrentUser = () => {
        Auth.currentAuthenticatedUser().then(user => {
          // if user is authenticated update authStatus & user state here
          this.setState({
            authStatus: AuthStatus.isLoggedIn,
            user: user.attributes.email
          })
        }).catch(e => {
          // if user is not authenticated update authStatus
          this.setState({ authStatus: AuthStatus.isNotLoggedIn })
        });
      }
    
      render() {
        const { authStatus } = this.state;
        switch (authStatus) {
          case AuthStatus.isLoggedIn:
            return (
              <div>
                <LoggedInContainer />
              </div>
            )
          case AuthStatus.isNotLoggedIn:
            return (
              <div>
                <LoginForm />
              </div>
            )
          default: return null;
          // when authstatus is Uninitialized or not isLoggedIn and not isNotLoggedIn null will be rendered
          // Rendering null while waiting on some async function is not ideal
        }
      }
    }
    
    export default AuthenticationShell;