React Native 如何在重新加载应用程序后保持用户登录?

sg2wtvxw  于 5个月前  发布在  React
关注(0)|答案(1)|浏览(84)

如何在重新加载应用程序或关闭应用程序后保持应用程序的当前注册用户登录在应用程序中?当前状态会在重新加载应用程序后将用户直接发送到我的登录屏幕,但是,如果我只是关闭它,(在iPhone上向上滑动EX)它可以让用户保持登录没有问题。我正在使用AsyncStorage来捕获并稍后在不同屏幕或历史用户中访问这些信息,然而,在realod之后,我似乎无法弄清楚如何实现这一点。对于上下文,我使用Expo与IOS模拟器。下面你会发现我的App.js,Login.js屏幕和DBAdapter组件用于从我的数据库中拉/添加用户信息。
App.js:

import React, { createContext, useState, useEffect } from 'react';
import { StyleSheet, } from 'react-native';
import BarCodeScan from './screens/BScanner';
import Favorites from './screens/Favorites';
import ProductInfo from './screens/ProductInfo';
import BottomTabs from './components/BottomTabs';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import TermsAndService from './screens/terms_and_service';
import { ListProvider } from './screens/ListContext';
import AboutUs from './screens/About Us';
import ProductDetailScreen from './screens/ProductDetail';
import WikiScreen from './screens/WikiScreen';
import Nutrition from './screens/Nutrition';
import Login from './components/Login';
import { initializeDB } from './DBA/DBAdapter';
import ChangePassword from './components/ChangePassword';
import Settings from './screens/Settings';
import UserProfile from './screens/UserProfile';
import AsyncStorage from '@react-native-async-storage/async-storage';

const Stack = createStackNavigator();

export const GlobalContext = createContext();

export default function App() {
  const [user, setUser] = useState(null);

  const loginUser = (username) => {
    //console.log('Logging in user:', username);
    const user = { id: 1, name: username,  };
    setUser(user);
  };

  const logoutUser = () => {
    setUser(null);
  };

  useEffect(() => {
    initializeDB();

    const loadUserData = async () => {
      try {
        const userData = await AsyncStorage.getItem('userData');
        if (userData) {
          const parsedUserData = JSON.parse(userData);
          loginUser(parsedUserData.username);
        } else {
          console.log('No user data found in AsyncStorage.');
        }
      } catch (error) { 
        console.error('Error loading user data from AsyncStorage:', error);
      }
    };
    
    loadUserData();
  }, []);

  return (
    <GlobalContext.Provider value={{ loginUser, logoutUser }}>
      <ListProvider>
        <NavigationContainer>
          <Stack.Navigator initialRouteName="Login">
            <Stack.Screen name="BottomTabs" options={{ headerShown: false, title: 'Back'}} component={({ navigation }) => ( <BottomTabs screenProps={{ navigation }} /> )} />
            <Stack.Screen name="Login" component={Login} options={{ headerShown: false }} />
            <Stack.Screen name="ProductInfo" component={ProductInfo} options={{ title: 'Product Details' }} />
            <Stack.Screen name="WikiScreen" component={WikiScreen} options={{ title: 'Wikipedia' }} />
            <Stack.Screen name="ProductDetails" component={ProductDetailScreen} options={{ title: 'Product Details' }} />
            <Stack.Screen name="Nutrition" component={Nutrition} options={{ title: 'Nutritional Facts' }} />
            <Stack.Screen name="ChangePassword" component={ChangePassword} options={{ headerShown: false }} />
            <Stack.Screen name="BarCodeScan" component={BarCodeScan} />
            <Stack.Screen name="TermsAndService" component={TermsAndService} />
            <Stack.Screen name="About Us" component={AboutUs} />
            <Stack.Screen name="Favorites" component={Favorites} />
            <Stack.Screen name="Settings" component={Settings} options={{ title: 'Settings' }} />
            <Stack.Screen name="UserProfile" component={UserProfile} options={{ title: 'User Profile' }} />
          </Stack.Navigator>
        </NavigationContainer>
      </ListProvider>
    </GlobalContext.Provider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

字符串
Login.js:

import React, { useContext, useState } from 'react';
import { View, TextInput, TouchableOpacity, Text, StyleSheet, Keyboard, TouchableWithoutFeedback, Image, } from 'react-native';
import Toast from 'react-native-toast-message';
import { verifyUser, addUser } from '../DBA/DBAdapter';
import { useNavigation } from '@react-navigation/native';
import { GlobalContext } from '../App';
import AsyncStorage from '@react-native-async-storage/async-storage';
import Icon from 'react-native-vector-icons/FontAwesome';

const Login = () => {
  const [isLogin, setIsLogin] = useState(true);
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [email, setEmail] = useState('');
  const navigation = useNavigation();
  const { loginUser } = useContext(GlobalContext);
  const [loginError, setLoginError] = useState('');
  const [registerError, setRegisterError] = useState('');
  const [showPassword, setShowPassword] = useState(false);

  const showToast = (message) => {
    Toast.show({
      text1: message,
      position: 'top',
      type: 'error',
      topOffset: 100,
      borderColor: 'red',
      duration: 1000,
    });
  };

  const handleToggle = () => {
    setIsLogin(!isLogin);
    setUsername('');
    setPassword('');
    setEmail('');
    setLoginError('');
    setRegisterError('');
  };

  const handleLogin = () => {
    if (username.length === 0 || password.length === 0) {
      setLoginError('Please fill in both username and password.');
      showToast('Please fill in both username and password.');
    } else if (username.length <= 7) {
      setLoginError('Username should be at least 8 characters long.');
      showToast('Username should be at least 8 characters long.');
    } else if (password.length <= 7) {
      setLoginError('Password should be at least 8 characters long.');
      showToast('Password should be at least 8 characters long.');
    } else {
      verifyUser(username, password, (result) => {
        if (result.success) {
          loginUser(result.user);
          const userData = result.user;
          AsyncStorage.setItem('userData', JSON.stringify(userData));
          navigation.navigate('BottomTabs', { user: result.user });
        } else {
          setLoginError('Invalid credentials. Please check your username and password.');
          showToast('Invalid credentials. Please check your username and password.');
        }
      });
    }
  };

  const isValidEmail = (email) => {
    const emailPattern = /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/;
    return emailPattern.test(email);
  };

  const handleRegister = () => {
    if (username.length === 0) {
        setRegisterError('Please fill in the username.');
        showToast('Please fill in the username.');
    } else if (password.length === 0) {
        setRegisterError('Please fill in the password.');
        showToast('Please fill in the password.');
    } else if (email.length === 0 || !isValidEmail(email)) {
        setRegisterError('Email field is invalid.');
        showToast('Email field is invalid.');
    } else if (username.length <= 7) {
        setRegisterError('Username should be at least 8 characters long.');
        showToast('Username should be at least 8 characters long.');
    } else if (password.length <= 7) {
        setRegisterError('Password should be at least 8 characters long.');
        showToast('Password should be at least 8 characters long.');
    } else {
        addUser(username, password, email, (result) => {
            if (result.success) {
                loginUser(result.user);
                const userData = {
                  username,
                  password,
                  email,
                };
                AsyncStorage.setItem('userData', JSON.stringify(userData));
                navigation.navigate('BottomTabs', { user: result.user });
            } else {
                setRegisterError(result.error);
                showToast(result.error);
            }
        });
    }
};

  const handleClear = () => {
    setUsername('');
    setPassword('');
    setEmail('');
  };

  return (
    <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
    <View style={styles.container}>
    <View style={styles.imageContainer}>
      <Image source={require('../assets/halalfinds2.png')} style={styles.image} resizeMode="contain" />
    </View>
        <View style={{ backgroundColor: "white", paddingBottom: 45 }}>
            <Text
            style={{
                fontWeight: 'bold',
                textAlign: 'center',
                fontSize: 30,
                paddingTop: 30,
                color: "rgba(79, 193, 35, 1)",
            }}
            >HalalFinds</Text>
            <Text
            style={{
                fontWeight: 'bold',
                textAlign: 'center',
                fontSize: 22,
                paddingTop: 10,
                color: "rgba(164, 164, 164, 1)",
            }}
            >Find Halal Products Easier</Text>
        </View>  
      <Toast />
      <View style={styles.toggleContainer}>
        <TouchableOpacity
          style={[styles.toggleButton, isLogin ? styles.activeButton : null]}
          onPress={handleToggle}
          disabled={isLogin}
        >
          <Text
            style={{
              ...styles.toggleButtonText,
              color: isLogin ? 'white' : 'gray',
            }}
          >
            Login
          </Text>
        </TouchableOpacity>
        <TouchableOpacity
          style={[
            styles.toggleButton,
            !isLogin ? styles.activeButton : null,
          ]}
          onPress={handleToggle}
          disabled={!isLogin}
        >
          <Text
            style={{
              ...styles.toggleButtonText,
              color: isLogin ? 'gray' : 'white',
            }}
          >
            Register
          </Text>
        </TouchableOpacity>
      </View>
      <View>
        <TextInput
          style={styles.input}
          placeholder="Username or email"
          onChangeText={(text) => setUsername(text)}
          value={username}
          placeholderTextColor="gray"
        />
        <View style={styles.passwordInput}>
            <TextInput
              style={styles.passInput}
              placeholder="Password"
              onChangeText={(text) => setPassword(text)}
              value={password}
              secureTextEntry={!showPassword} // Toggle secureTextEntry based on showPassword state
              placeholderTextColor="gray"
            />
            <TouchableOpacity
              onPress={() => setShowPassword(!showPassword)}
              style={styles.eyeIcon}
            >
              <Icon
                name={showPassword ? 'eye' : 'eye-slash'}
                size={20}
                color="gray"
              />
            </TouchableOpacity>
            </View>
        {!isLogin && (
          <TextInput
            style={styles.input}
            placeholder="Email"
            onChangeText={(text) => setEmail(text)}
            value={email}
            secureTextEntry={false}
            placeholderTextColor="gray"
          />
        )}
        <Text style={styles.errorText}>{isLogin ? loginError : registerError}</Text>
        <View style={styles.buttonContainer}>
          {username.length > 0 && (
            <TouchableOpacity
              style={styles.clearButton}
              onPress={handleClear}
            >
              <Text style={styles.clearButtonText}>Clear</Text>
            </TouchableOpacity>
          )}
          <TouchableOpacity
            style={styles.button}
            onPress={isLogin ? handleLogin : handleRegister}
          >
            <Text style={styles.buttonText}>
              {isLogin ? 'Login' : 'Register'}
            </Text>
          </TouchableOpacity>
        </View>
      </View>
    </View>
    </TouchableWithoutFeedback>
  );
};

const styles = StyleSheet.create({
    imageContainer: {
        alignItems: 'center',
        justifyContent: 'center',
        marginBottom: 20,
      },
      image: {
        width: 160,
        height: 160,
      },
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingHorizontal: 16,
    backgroundColor: 'white',
    marginBottom: 60,
  },
  toggleContainer: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    marginBottom: 16,
  },
  toggleButton: {
    color: 'rgba(79, 193, 35, 1)',
    paddingHorizontal: 16,
    paddingVertical: 8,
    borderRadius: 8,
    borderWidth: 1,
    borderColor: '#888',
  },
  activeButton: {
    backgroundColor: 'rgba(79, 193, 35, 1)',
    borderColor: 'rgba(79, 193, 35, 1)',
  },
  toggleButtonText: {
    color: 'black',
    fontSize: 16,
    fontWeight: 'bold',
  },
  input: {
    height: 40,
    borderColor: 'gray',
    borderWidth: 1,
    marginBottom: 16,
    paddingHorizontal: 10,
    borderRadius: 8,
    color: 'black',
  },
  buttonContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginBottom: 16,
  },
  button: {
    flex: 1,
    backgroundColor: 'rgba(79, 193, 35, 1)',
    borderRadius: 8,
    paddingVertical: 12,
  },
  clearButton: {
    flex: 1,
    backgroundColor: 'red',
    borderRadius: 8,
    paddingVertical: 12,
    marginRight: 8,
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  clearButtonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: 'bold',
    textAlign: 'center',
  },
  errorText: {
    color: 'red',
    marginBottom: 16,
  },
  passwordInput: {
    flexDirection: 'row',
    alignItems: 'center',
    borderWidth: 1,
    borderColor: 'gray',
    borderRadius: 8,
    marginBottom: 16,
    paddingHorizontal: 10,
  },
  passInput: {
    flex: 1,
    height: 40,
    borderColor: 'gray',
    borderWidth: 0,
    borderRadius: 0,
    marginBottom: 0,
    paddingHorizontal: 0,
  },
  eyeIcon: {
    padding: 10,
  },
});

export default Login;


DBAdapter.js:

import * as SQLite from 'expo-sqlite';
const db = SQLite.openDatabase('Main.db');

export const initializeDB = () => {
    return new Promise((resolve, reject) => {
        db.transaction((tx) => {
            tx.executeSql(
                "CREATE TABLE IF NOT EXISTS Users (ID INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT, email TEXT, joinedDate TEXT);",
                [],
                () => {
                    console.log('Table Created Successfully');
                    resolve();
                },
                (tx, error) => {
                    console.log('Error Creating Table:', error);
                    reject(error);
                }
            );
        });
    });
};

export const addUser = (username, password, email, callback) => {
    db.transaction((tx) => {
        try {
            const joinedDate = new Date().toISOString().split('T')[0];

            tx.executeSql(
                "SELECT * FROM Users WHERE username = ? OR email = ?",
                [username, email],
                (_, { rows }) => {
                    if (rows.length > 0) {
                        callback({ success: false, error: 'User with the same credentials already exists, use Login' });
                    } else {
                        tx.executeSql(
                            "INSERT INTO Users (username, password, email, joinedDate) VALUES (?,?,?,?)",
                            [username, password, email, joinedDate],
                            (_, { insertId }) => {
                                const user = {
                                    username,
                                    password,
                                    email,
                                    joinedDate
                                };
                                callback({ success: true, insertId, user });
                            },
                            (tx, e) => {
                                console.log('Error while inserting user: ', e);
                                callback({ success: false, error: 'Error inserting user' });
                            }
                        );
                    }
                },
                (tx, e) => {
                    console.log('Error checking existing users: ', e);
                    callback({ success: false, error: 'Error checking existing users' });
                }
            );
        } catch (error) {
            console.log('Error adding the user: ', error);
            callback({ success: false, error: 'Error adding the user' });
        }
    });
};

export const updatePassword = (username,newPassword) => {
    db.transaction((tx) => {
        try {
            tx.executeSql('UPDATE Users SET password = ? WHERE username = ?',[newPassword,username],()=>{
                console.log("Password Changed")
            },(tx,e)=>{
                console.log(e)
            })
        } catch {
            console.log("Error updating password")
        }
    })
}

export const getPassword = (username,callback) => {
    db.transaction((tx) => {
        try {
            tx.executeSql("Select * from Users WHERE username = ?",[username],
            (_,{ rows }) => {
                if (rows.length === 0) 
                {
                    callback({ success: false, error: 'Invalid Password' }); 
                } else {
                    const user = rows.item(0);
                    callback({success: true, password: user.password})
                }
            },(tx,e)=>{
                console.log(e)
            })
        } catch {
            console.log("Error updating password")
        }
    })
}

export const getUsername = (username, callback) => {
    db.transaction((tx) => {
      try {
        tx.executeSql(
          "SELECT * FROM Users WHERE username = ?",
          [username],
          (_, { rows }) => {
            if (rows.length === 0) {
              callback({ success: false, error: 'Invalid Username' });
            } else {
              const user = rows.item(0);
              callback({ success: true, username: user.username });
            }
          },
          (tx, e) => {
            console.log(e);
            callback({ success: false, error: 'Error fetching username' });
          }
        );
      } catch (error) {
        console.log('Error fetching username', error);
        callback({ success: false, error: 'Error fetching username' });
      }
    });
  };  

  export const verifyUser = (username, password, callback) => {
    db.transaction((tx) => {
      try {
        tx.executeSql(
          "Select * from Users WHERE username = ?",
          [username],
          (_, { rows }) => {
            if (rows.length === 0) {
              callback({ success: false, error: 'Invalid Username', user: null });
            } else {
              const user = rows.item(0);
              if (user.password === password) {
                callback({ success: true, user });
              } else {
                callback({ success: false, error: 'Invalid Password', user: null });
              }
            }
          },
          (_, e) => {
            console.log(e);
            callback({ success: false, error: 'Error verifying the user', user: null });
          }
        );
      } catch {
        console.log("Error verifying the user");
        callback({ success: false, error: 'Error verifying the user', user: null });
      }
    });
  };


我尝试在App.js的loginUser方法中调用userData,但是,它仍然没有保存重新加载后登录的用户。我希望即使重新加载应用程序后,注册的用户也可以保持登录状态,而不必一遍又一遍地输入他们的凭据。

6rqinv9w

6rqinv9w1#

你必须修改你的应用导航架构。
典型的应用程序有两个公共屏幕(入职/欢迎,登录,注册,忘记密码,政策,.).
其他需要用户进行身份验证的屏幕应视为私有。
您可以根据React Navigation -https://reactnavigation.org/docs/auth-flow推荐的用户身份验证状态渲染PUBLIC/PRIVATE屏幕
应用架构的简化版本:

import React, { createContext, useState, useEffect } from "react";
import { StyleSheet, LoadingIndicator } from "react-native";
import BarCodeScan from "./screens/BScanner";
import Favorites from "./screens/Favorites";
import ProductInfo from "./screens/ProductInfo";
import BottomTabs from "./components/BottomTabs";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import TermsAndService from "./screens/terms_and_service";
import { ListProvider } from "./screens/ListContext";
import AboutUs from "./screens/About Us";
import ProductDetailScreen from "./screens/ProductDetail";
import WikiScreen from "./screens/WikiScreen";
import Nutrition from "./screens/Nutrition";
import Login from "./components/Login";
import { initializeDB } from "./DBA/DBAdapter";
import ChangePassword from "./components/ChangePassword";
import Settings from "./screens/Settings";
import UserProfile from "./screens/UserProfile";
import AsyncStorage from "@react-native-async-storage/async-storage";

const Stack = createStackNavigator();

export const GlobalContext = createContext();

export default function App() {
  const [user, setUser] = useState(null);
  const [authenticating, setAuthenticating] = useState(true);

  const loginUser = (username) => {
    //console.log('Logging in user:', username);
    const user = { id: 1, name: username };
    setUser(user);
  };

  const logoutUser = () => {
    setUser(null);
  };

  useEffect(() => {
    initializeDB();

    const loadUserData = async () => {
      try {
        const userData = await AsyncStorage.getItem("userData");
        setAuthenticating(false);
        if (userData) {
          const parsedUserData = JSON.parse(userData);
          loginUser(parsedUserData.username);
        } else {
          console.log("No user data found in AsyncStorage.");
        }
      } catch (error) {
        console.error("Error loading user data from AsyncStorage:", error);

        setAuthenticating(false);
      }
    };

    loadUserData();
  }, []);

  const isAuthenticated = user !== null;

  if (authenticating) {
    return <LoadingIndicator size="large" />;
  }

  return (
    <GlobalContext.Provider value={{ loginUser, logoutUser }}>
      <ListProvider>
        <NavigationContainer>
          <Stack.Navigator initialRouteName="Login">
            {isAuthenticated ? (
              <>
                <Stack.Screen
                  name="BottomTabs"
                  options={{ headerShown: false, title: "Back" }}
                  component={({ navigation }) => (
                    <BottomTabs screenProps={{ navigation }} />
                  )}
                />
                <Stack.Screen
                  name="ProductInfo"
                  component={ProductInfo}
                  options={{ title: "Product Details" }}
                />
                <Stack.Screen
                  name="WikiScreen"
                  component={WikiScreen}
                  options={{ title: "Wikipedia" }}
                />
                <Stack.Screen name="BarCodeScan" component={BarCodeScan} />
                <Stack.Screen
                  name="Nutrition"
                  component={Nutrition}
                  options={{ title: "Nutritional Facts" }}
                />
                <Stack.Screen
                  name="Settings"
                  component={Settings}
                  options={{ title: "Settings" }}
                />
                <Stack.Screen
                  name="UserProfile"
                  component={UserProfile}
                  options={{ title: "User Profile" }}
                />
              </>
            ) : (
              <>
                {" "}
                <Stack.Screen
                  name="Login"
                  component={Login}
                  options={{ headerShown: false }}
                />
                <Stack.Screen
                  name="ProductDetails"
                  component={ProductDetailScreen}
                  options={{ title: "Product Details" }}
                />
                <Stack.Screen
                  name="ChangePassword"
                  component={ChangePassword}
                  options={{ headerShown: false }}
                />
                <Stack.Screen
                  name="TermsAndService"
                  component={TermsAndService}
                />
                <Stack.Screen name="About Us" component={AboutUs} />
                <Stack.Screen name="Favorites" component={Favorites} />{" "}
              </>
            )}
          </Stack.Navigator>
        </NavigationContainer>
      </ListProvider>
    </GlobalContext.Provider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

字符串

相关问题