React Native实现持久登录:AsyncStorage功能求助
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-keychainfor secure storage.
内容的提问来源于stack exchange,提问作者Haseeb Javaid

