Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling logout properly with React Native

I have a react native application that show's different navigators based on whether or not there is an authenticated firebase user.

Here's my main App class:

export default class App extends Component {
  state = { loggedIn: null }

  componentWillMount() {
    firebase.initializeApp({...});

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

  renderContent() {
    switch (this.state.loggedIn) {
      case true:
        return (
          <MenuContext>
            <MainNavigator />
          </MenuContext>
        );
      case false:
        return (
          <AuthNavigator />
        );
      default:
        return (
          <View style={styles.spinnerStyle}>
            <Spinner size="large" />
          </View>
        );
    }
  }

  render() {
    return (
      <Provider store={store}>
        {this.renderContent()}
      </Provider>
    );
  }
}

Now this works perfectly fine with re-rendering the correct Navigator when a user logs in.

The problem I have is with logging out. In my MainNavigator (the one that shows when a user is logged in), I have a logout button which logs the user out. Implemented like this:

signOutUser = async () => {
    try {
        await firebase.auth().signOut();
    } catch (e) {
        console.log(e);
    }
}

render() {
    return (
        ...
        <Button title="logout" onPress={() => this.signOutUser()} />
    )
}

This does what is expected, however, I get the following warning which I want to resolve:

Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState or forceUpdate on an unmounted component. This is a no-op.

What is the best practice way of implementing this logout functionality?

Edit: I'm using the react-navigation library for managing my application navigation.

like image 948
Barry Michael Doyle Avatar asked Aug 08 '17 09:08

Barry Michael Doyle


1 Answers

I think generating your Navigator depending on a state of App component can be dangerous and can lead to this issue. On logout you should redirect to the component in charge of registration or login and don't let the state in charge of this with regenerating a part of an another navigation.

What you can do is in your App Component, instead of returning MainNavigator or AuthNavigator, you can return both Navigator and add a Route called Loading for example, who will render a loading screen and who will be the initialRoute in charge of routing your app depending on the user is logged or not.

Example of StackNavigator with this case

const Navigator = StackNavigator({
  Main: {
    screen: MainComponent,
  },
  Auth: {
    screen: AuthComponent,
  },
  Loading: {
    screen: LoadingComponent
  },
},{
    initialRouteName : 'Loading'
});

in your LoadingComponent you will render your spinner

render() {
  return(
    <View style={styles.spinnerStyle}>
      <Spinner size="large" />
    </View>
  );
}

And in componentDidMount of LoadingComponent :

firebase.auth().onAuthStateChanged(user => {
  if (user) {
      navigate('Home');
  } else {
      navigate('Login');
  }
});

With that the loading screen will be display when you open your app and then redirect to the good page depending of user is logged or not.

And in your signOutUser redirect to Login page

signOutUser = async () => {
    try {
        await firebase.auth().signOut();
        navigate('Auth');
    } catch (e) {
        console.log(e);
    }
}
like image 118
Gabriel Diez Avatar answered Oct 09 '22 21:10

Gabriel Diez