Amplify-js: understanding React SignOut hoc - doesn't work until reload

Created on 19 Jul 2018  路  14Comments  路  Source: aws-amplify/amplify-js

Now that there's a SignOut hoc in aws-amplify-react 1.0.x I tried a simple test by inserting it as a component in a fresh create-react-app App.js (that is also wrapped by withAuthenticator)

If I click the "Sign Out" button that it creates nothing happens - the authData and authState are not affected until I manually reload the page in the browser, they they're cleared.

Am I misunderstanding that the point of this component to fully trigger a signout sequence, including a rerender or redirect once the state is updated? If not why not just build your own call to Auth.signOut() that includes the rerender or redirect?

Auth documentation

Most helpful comment

Hi, here is how I handle it (I had to read aws-amplify-react code to understand the behavior)

I use a react context to have a reference on the method to change state of the Authenticator

class App extends Component {
  static propTypes = {
    onStateChange: PropTypes.func,
  };

  render() {
    return (
      <ThemeProvider theme={theme} key="main">
        <UserContext.Provider
          value={{
            onStateChange: this.props.onStateChange,
          }}
        >
          <Router>
            <Home path="/" />
          </Router>
        </UserContext.Provider>
      </ThemeProvider>
    );
  }
}


export default withAuthenticator(App, false, [
  <Greetings key="greetings" />, //checks if the user is already connected
  <SignIn key="signin" />, //sign in
  <ConfirmSignIn key="confirmSignin" />, //added to customize next one
  <RequireNewPassword key="requireNewPassword" />, //new password is required
]);
Then I get back the method on sign out button


     signOut = onStateChange => async () => {
             await Auth.signOut();
            onStateChange('signedOut');
       };


      render = () => (
               <UserContext.Consumer>
                    {({ onStateChange }) => (
                      <button
                        className="disconnect"
                        onClick={this.signOut(onStateChange)}
                      >
                        Se d茅connecter
                      </button>
                    )}
                  </UserContext.Consumer>
       )

An redirect works fine.

First time I used amplify it was with react native. I was very surprise with amplify-react behavior. On react native I did not have to make all this stuff.

All 14 comments

@tunecrew when you press signout it should redirect to login screen. Can you share a code snippet?

so created a generic app using npx create-react-app . ; npm install aws-amplify-react and edited App.js as follows:

import React, { Component } from 'react';
import Amplify from 'aws-amplify';
import { withAuthenticator, SignOut } from 'aws-amplify-react';

const awsconfig = {
  Auth: {
    identityPoolId: ***redacted***,
    region: ***redacted***,
    userPoolId: ***redacted***,
    userPoolWebClientId: ***redacted***,
  },
}

Amplify.configure(awsconfig)

class App extends Component {
  render() {
    return (
      <div className="App">
        <SignOut/>
      </div>
    );
  }
}

export default withAuthenticator(App);

Authentication works fine and once signed in the SignOut button is presented.

Clicking on it, however, only clears the tokens in local storage, it does not redirect anywhere.

What's the expected behaviour in this case? Am I missing some other piece?

We have a similar situation when we call amplify's Auth.signOut() method manually. Previous to v1 it would result in the Authenticator component's authState updating and trigger an render in our app where it would show the correct screen.

import * as React from 'react';
import Auth from '@aws-amplify/auth';

Here's a simplified version of our component:

class ButtonSignOut extends React.Component {
    handleSignOut = (e) => {
        e.preventDefault();
        Auth.signOut();
    }
    render() {
        return (
            <button onClick={this.handleSignOut}>
                Sign Out
            </button>
        );
    }
}

It doesn't seem to do this anymore and I'm also strugging to find the solution. Are we supposed to manually specify the authState using the Authenticators onStateChange method?

Yep, we also have the same issue and have tried settingState and even forcingUpdate, but to no avail.

Looks like this has a workround:
https://github.com/aws-amplify/amplify-js/issues/245

Hi, here is how I handle it (I had to read aws-amplify-react code to understand the behavior)

I use a react context to have a reference on the method to change state of the Authenticator

class App extends Component {
  static propTypes = {
    onStateChange: PropTypes.func,
  };

  render() {
    return (
      <ThemeProvider theme={theme} key="main">
        <UserContext.Provider
          value={{
            onStateChange: this.props.onStateChange,
          }}
        >
          <Router>
            <Home path="/" />
          </Router>
        </UserContext.Provider>
      </ThemeProvider>
    );
  }
}


export default withAuthenticator(App, false, [
  <Greetings key="greetings" />, //checks if the user is already connected
  <SignIn key="signin" />, //sign in
  <ConfirmSignIn key="confirmSignin" />, //added to customize next one
  <RequireNewPassword key="requireNewPassword" />, //new password is required
]);
Then I get back the method on sign out button


     signOut = onStateChange => async () => {
             await Auth.signOut();
            onStateChange('signedOut');
       };


      render = () => (
               <UserContext.Consumer>
                    {({ onStateChange }) => (
                      <button
                        className="disconnect"
                        onClick={this.signOut(onStateChange)}
                      >
                        Se d茅connecter
                      </button>
                    )}
                  </UserContext.Consumer>
       )

An redirect works fine.

First time I used amplify it was with react native. I was very surprise with amplify-react behavior. On react native I did not have to make all this stuff.

Any comments from the crew on this one? It seems to me that there is a bug in the withAuthenticator() HOC, or am I missing something? Calling signOut() should definitely "reset" the state of the whole application, making it appear as if the current user is not logged on, without manually forcing a page reload.

Attempted to dispath a signedOut event via the Hub, but with no luck. This event is not being consumed by the provided <Greeting> control.

     Hub.dispatch('auth', { event: 'signedOut', data: undefined }, 'Auth');

@jonasao Auth.signOut() only clears the tokens stored in cache. In order to change the state back to signIn when using withAuthenticator or Authenticator, you need to do something like:

Auth.signOut().then(() => {
      if (this.props.onStateChange) { this.props.onStateChange('signIn', null); }
});

@powerful23 Thanks, you made my day! Unfortunately I think the "Amplify Team" should have a look at this - either adding your information to their docs, or by changing the Auth.signOut() method.

Proposed change to the Auth.signOut() method:
Add an argument/flag, which allows the user to completely sign out the user when using a single-page application using withAuthenticator or Authenticator. It should be unnecessary for the developer to have to add the extra code as mentioned in the example above.

@jonasao I agree we need either enhance the doc or we can make the Authenticator listen on the Hub event so that when a signOut event emitted from the library, it will change the state back to signIn.

Maybe not very elegant solution, but I extended the SignOut component to change the render, and using this component instead of original one, so far it works until there is a fix published:

import React from 'react';
import { SignOut as base } from 'aws-amplify-react';

class SignOut extends base {
  render() {
    const { hide } = this.props;
    if (hide && hide.includes(SignOut)) {
      return null;
    }

    const { authState } = this.state;
    const signedIn = authState === 'signedIn';

    if (!signedIn) {
      return null;
    }

    const signOutElement = this.props.children || 'Sign Out';

    return (
      <a href="/" onClick={this.signOut} style={this.props.style}>
        {signOutElement}
      </a>
    );
  }
}

export default SignOut;

The trick is having <a> tag with both href and onClick, and it runs both. You can pass inline or block-type components as children to this new SignOut component.

Seems like this section:
https://aws-amplify.github.io/docs/js/authentication#customize-ui

Should address this, please let me know/feel free to re-open otherwise. Will take a look at the signOut method as well and see if we can address/enhance (moving to our backlog).

I can't understand why anyone would suggest that signing out with Auth.signOut() should cause AWS Amplify to automatically reload the page (which it currently does today!). React applications are single page applications. The developer should absolutely be responsible for and have control over updating the application state as necessary to reset the app upon sign out. If the the developer decides they need to reload the page in order to do that, then so be it, but this should be behavior we have control over.

Hi,

I am seeing the same issue where I am able to sign out but the app does not render. I tried the solution proposed by @powerful23 as below, but it does not seem to work:

import {Greetings} from 'aws-amplify-react-native';

export default class MainScreen extends Greetings {
  signOut() {
    Auth.signOut().then(() => {
      if (this.props.onStateChange) { this.props.onStateChange('signIn', null); console.log('change state')}
      console.log('inside');
      this.onStateChange('signIn', null)
}).catch(err => console.log('errored out:' + err));

<TouchableHighlight style={[styles.buttonContainer]} onPress={() => this.signOut()}>
              <Text style={styles.signupText}>Sign out</Text>
            </TouchableHighlight>

I can see the user being signed out. but it never goes into the if statement:
if (this.props.onStateChange)

I added this.onStateChange('signIn', null) outside the if statement but I get the error below:

TypeError: _this2.onStateChange is not a function. (In '_this2.onStateChange('signIn', null)', '_this2.onStateChange' is undefined)

I am new to aws-react-native and react-native. Any help is appreciated.

When using <AmplifySignOut />, a user is redirected to the login page after signing out. This is the expected UX. However, when using Auth.signOut() this does not happen. It would be convenient if there was an option when calling signOut to trigger the same behavior.

Was this page helpful?
0 / 5 - 0 ratings