You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native实现持久登录:AsyncStorage功能求助

Implementing Persistent Login with AsyncStorage in React Native

Hey there! Since you're already up and running with your PHP/MySQL login flow, adding persistent login is totally doable with AsyncStorage. Let's break this down into simple, actionable steps that fit right into your existing code.

Core Idea

When a user logs in successfully, we'll save their authenticated state (like their email or a session token) to AsyncStorage. Every time the app launches, we'll first check if this data exists—if it does, we'll send them straight to the Profile screen; if not, we'll show the Login screen.

Step 1: Save User Data on Successful Login

Update your UserLoginFunction to store the user's email once the login is confirmed. We'll use async/await since AsyncStorage operations are asynchronous:

UserLoginFunction = async () => {
  const { UserEmail, UserPassword } = this.state;

  try {
    const response = await fetch('http://192.168.0.107/loginrn/User_Login.php', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        email: UserEmail,
        password: UserPassword
      })
    });

    const responseJson = await response.json();

    if (responseJson === 'Data Matched') {
      // Save user email to AsyncStorage
      await AsyncStorage.setItem('loggedInUser', UserEmail);
      // Navigate to profile screen
      this.props.navigation.navigate('Second', { Email: UserEmail });
    } else {
      Alert.alert(responseJson);
    }
  } catch (error) {
    console.error(error);
    Alert.alert('Error', 'Something went wrong, please try again');
  }
};

Step 2: Add an Initial Screen to Check Login State

We'll create a wrapper screen that checks AsyncStorage when the app starts, then navigates to the correct screen. Replace your existing navigator setup with this:

// New initial screen to handle login check
class InitialScreen extends Component {
  componentDidMount() {
    this.checkLoginStatus();
  }

  checkLoginStatus = async () => {
    try {
      const userEmail = await AsyncStorage.getItem('loggedInUser');
      if (userEmail) {
        // User is already logged in, go to profile
        this.props.navigation.navigate('Second', { Email: userEmail });
      } else {
        // No saved session, go to login
        this.props.navigation.navigate('First');
      }
    } catch (error) {
      console.error('Failed to load login status:', error);
      // Fallback to login screen if there's an error
      this.props.navigation.navigate('First');
    }
  };

  render() {
    // You can replace this with a loading spinner for better UX
    return (
      <View style={styles.MainContainer}>
        <Text>Loading...</Text>
      </View>
    );
  }
}

// Update stack navigator to include the initial screen
const AppNavigator= createStackNavigator(
  {
    Initial: { screen: InitialScreen },
    First: { screen: LoginActivity },
    Second: { screen: ProfileActivity }
  },
  {
    initialRouteName: 'Initial' // Set InitialScreen as the app's starting point
  });

Step 3: Implement Logout to Clear Saved Data

Update your ProfileActivity's logout button to remove the saved user data from AsyncStorage:

class ProfileActivity extends Component {
  static navigationOptions = {
    title: 'ProfileActivity',
  };

  handleLogout = async () => {
    try {
      // Remove the saved user entry
      await AsyncStorage.removeItem('loggedInUser');
      // Navigate back to login screen
      this.props.navigation.navigate('First');
    } catch (error) {
      console.error('Failed to logout:', error);
      Alert.alert('Error', 'Could not logout, please try again');
    }
  };

  render() {
    return(
      <View style = { styles.MainContainer }>
        <Text style = {styles.TextComponentStyle}>
          { this.props.navigation.state.params.Email }
        </Text>
        <Button title="Click here to Logout" onPress={this.handleLogout} />
      </View>
    );
  }
}

Full Modified Code

Here's how all the pieces fit together, including the styles you had:

import React, { Component } from 'react';
import { StyleSheet, TextInput, View, Alert, Button, Text , AsyncStorage} from 'react-native';
import { createStackNavigator , createAppContainer } from 'react-navigation';

// Login Activity
class LoginActivity extends Component {
  static navigationOptions = {
    title: 'LoginActivity',
  };

  constructor(props) {
    super(props)
    this.state = {
      UserEmail: '',
      UserPassword: ''
    }
  }

  UserLoginFunction = async () => {
    const { UserEmail, UserPassword } = this.state;

    try {
      const response = await fetch('http://192.168.0.107/loginrn/User_Login.php', {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          email: UserEmail,
          password: UserPassword
        })
      });

      const responseJson = await response.json();

      if (responseJson === 'Data Matched') {
        await AsyncStorage.setItem('loggedInUser', UserEmail);
        this.props.navigation.navigate('Second', { Email: UserEmail });
      } else {
        Alert.alert(responseJson);
      }
    } catch (error) {
      console.error(error);
      Alert.alert('Error', 'Something went wrong, please try again');
    }
  }

  render() {
    return (
      <View style={styles.MainContainer}>
        <Text style= {styles.TextComponentStyle}>User Login Form</Text>
        <TextInput
          placeholder="Enter User Email"
          onChangeText={UserEmail => this.setState({UserEmail})}
          underlineColorAndroid='transparent'
          style={styles.TextInputStyleClass}
        />
        <TextInput
          placeholder="Enter User Password"
          onChangeText={UserPassword => this.setState({UserPassword})}
          underlineColorAndroid='transparent'
          style={styles.TextInputStyleClass}
          secureTextEntry={true}
        />
        <Button title="Click Here To Login" onPress={this.UserLoginFunction} color="#2196F3" />
      </View>
    );
  }
}

// Profile Activity
class ProfileActivity extends Component {
  static navigationOptions = {
    title: 'ProfileActivity',
  };

  handleLogout = async () => {
    try {
      await AsyncStorage.removeItem('loggedInUser');
      this.props.navigation.navigate('First');
    } catch (error) {
      console.error('Failed to logout:', error);
      Alert.alert('Error', 'Could not logout, please try again');
    }
  };

  render() {
    return(
      <View style = { styles.MainContainer }>
        <Text style = {styles.TextComponentStyle}>
          { this.props.navigation.state.params.Email }
        </Text>
        <Button title="Click here to Logout" onPress={this.handleLogout} />
      </View>
    );
  }
}

// Initial Screen to check login status
class InitialScreen extends Component {
  componentDidMount() {
    this.checkLoginStatus();
  }

  checkLoginStatus = async () => {
    try {
      const userEmail = await AsyncStorage.getItem('loggedInUser');
      if (userEmail) {
        this.props.navigation.navigate('Second', { Email: userEmail });
      } else {
        this.props.navigation.navigate('First');
      }
    } catch (error) {
      console.error('Failed to load login status:', error);
      this.props.navigation.navigate('First');
    }
  };

  render() {
    return (
      <View style={styles.MainContainer}>
        <Text>Loading...</Text>
      </View>
    );
  }
}

// Stack Navigator Setup
const AppNavigator= createStackNavigator(
  {
    Initial: { screen: InitialScreen },
    First: { screen: LoginActivity },
    Second: { screen: ProfileActivity }
  },
  {
    initialRouteName: 'Initial'
  });

export default createAppContainer(AppNavigator);

// Styles
const styles = StyleSheet.create({
  MainContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
    padding: 20
  },
  TextInputStyleClass: {
    textAlign: 'center',
    height: 40,
    width: '100%',
    borderWidth: 1,
    borderColor: '#009688',
    borderRadius: 5 ,
    marginBottom: 10
  },
  TextComponentStyle: {
    fontSize: 20,
    textAlign: 'center',
    marginBottom: 15
  }
});

Quick Notes for Production

  • Security Upgrade: Storing an email is fine for testing, but in production, use a short-lived JWT token generated by your PHP backend instead. Validate this token on subsequent requests to ensure the session is still valid.
  • Loading UX: Replace the "Loading..." text with a spinner component (like from react-native-elements) to make the app feel more polished.
  • AsyncStorage Limitations: AsyncStorage is not encrypted by default. For sensitive data, consider using a library like react-native-keychain for secure storage.

内容的提问来源于stack exchange,提问作者Haseeb Javaid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 16:18:15