How Redirect to Login if page is protected and the user is not signed in?

时间秒杀一切 提交于 2019-12-08 09:33:21

问题


In my App I have some public screens that are accessible even if the user is not logged in, and some screens are protected (you must be logged in to access them).

My solution to the problem is to check the component willFocus Listener and if not logged in, the user should be redirected to the loginPage.

export async function ProtectRoute(navigation){

//if page will enter and the user is not authenticated return to login
navigation.addListener(
    'willFocus',
    async () => {
        let token = await getTokenAsync();

        if(!token){
            navigation.navigate('Login');
        }
    })
}

In my screen I Call this function in ComponentWillMount lifecycle. The issue is that it takes like a second to verify the token and the page is displayed briefly.

How can I make it so that he goes directly to the Login Page without that lag ?


回答1:


I wrote a quick example below. You can examine and use it.

import React, { Component } from "react";
import { Text, View } from "react-native";

const withAuth = WrappedComponent => {
  class AuthenticationScreen extends Component {
    constructor(props) {
      super(props);

      this.state = {
        isAuthenticated: false
      };

      props.navigation.addListener("willFocus", async () => {
        await this.checkAuth();
      });
    }

    remoteReuqest = async () => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(true);
        }, 2000);
      });
    };

    checkAuth = async () => {
      const result = await this.remoteReuqest();
      if (result) {
        this.setState({
          isAuthenticated: true
        });
      } else {
        this.props.navigation.navigate("Login");
      }
    };

    render() {
      if (!this.state.isAuthenticated) {
        return <Text>Waiting...</Text>;
      }
      return <WrappedComponent {...this.props} />;
    }
  }

  return AuthenticationScreen;
};

export default withAuth;

You can use it as follows.

import React, { Component } from "react";
import { Text, StyleSheet, View } from "react-native";
import withAuth from "./withAuth";

class ContactScreen extends Component {
  render() {
    return (
      <View>
        <Text> Contact Screen </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({});

const extendedComponent = withAuth(ContactScreen);

extendedComponent.navigationOptions = {
  title: "Contact"
};
export default extendedComponent;




回答2:


The issue is that it takes like a second to verify the token and the page is displayed briefly.

The reason is because reading/writing from/to AsyncStorage is an asychronous operation.

In my screen I Call this function in ComponentWillMount lifecycle.

I suggest you to not use ComponentWillMount lifecycle because it's deprecated and it will be removed from React (https://reactjs.org/docs/react-component.html#unsafe_componentwillmount)

After this introduction, now i show you how I have achieved this in my app: CONTEXT API! (https://reactjs.org/docs/context.html)

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

How to implement context api: the context will be the 'state' of your App.js file. You root App.js will be the provider of the context, while other views which will need the context are called the consumers of the context.

  1. First of all, you need to create a 'skeleton' of your context into a separate file, something like this:
// AuthContext.js
import React from 'react'
const AuthContext = React.createContext({
  isLogged: false,
  login: () => {},
  logout: () => {}
})
export default AuthContext
  1. Your App.js will import, contain and initialize the context:
// App.js
// all necessary imports
import AuthContext from '....'
export default class App extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      isAuth: false,
      login: this.login,
      logout: this.logout
    }

    login = async userData => {
    // do your login stuff and then
    this.setState({ isAuth: true })
    }

    logout = async () => {
    // do your logout stuff and then
    this.setState({ isAuth: false })
    }

    async ComponentDidMount () {
      // check the asyncStorage here and then, if logged:
      this.setState({ isAuth: true })
    }

    render () {
      return (
        <AuthContext.Provider value={this.state}>
          <AppContainer />
        </AuthContext.Provider>
      )
    }
  1. Then, into the View contained into AppContainer, you could access context like this:
import AuthContext from '.....'
// all necessary imports
export default class YourView extends React.Component<Props, State> {
  constructor (props) {
    super(props)
    this.props = props
    this.state = { ... }
  }

  // THIS IS IMPORTANT
  static contextType = AuthContext
  // with this, you can access the context through 'this.context'

  ComponentDidMount () {
    if (!this.context.isAuth) this.props.navigation.navigate('login')
  }

Advantages of this approach:

  1. Checking a boolean is so fast that you will not notice a blank screen.
  2. Sharing an Authentication Context everywhere in you app
  3. Making the access to asyncstorage only the first time that app mounts and not everytime you need to check if the user is logged
  4. Sharing methods to login/logout everywhere in your app


来源:https://stackoverflow.com/questions/55847623/how-redirect-to-login-if-page-is-protected-and-the-user-is-not-signed-in

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!