javascript - 使用 redux 和 asyncStorage 在 react-native 中进行身份验证和授权
问题描述
我是 react-native 和 react-redux 的新手。我正在开发一个反应原生项目,我想在其中实现身份验证和授权。在几毫秒内,我想显示启动画面,如果用户未登录,则重定向到登录页面,否则重定向到仪表板页面。如果用户注销,则导航到登录屏幕。我正在使用 react-native 0.62、react-navigation 5.x 和 redux 7.2。我为登录屏幕、忘记密码屏幕、otp 屏幕和重置密码屏幕创建了一个 stacknavigator。和一个抽屉导航器,其中包含仪表板屏幕、lms 屏幕、我的个人资料屏幕和注销。我已经使用 redux-thunk 实现了 api。我无法重定向到仪表板。目前,我已经使用 UseStatic 的静态值检查了条件。我不知道它是否正确。我在登录屏幕中记住了我的复选框。因此,如果用户选中该复选框,那么在用户注销之前,用户将不需要输入凭据。我筋疲力尽,因为我不知道什么是 redux 和 asyncStorage 以及如何使用它
App.js file
import 'react-native-gesture-handler';
enableScreens();
import React, { useState, useEffect } from 'react';
import { enableScreens } from 'react-native-screens';
import { StatusBar } from 'react-native';
import { Provider, connect } from 'react-redux';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import store from '../store/store';
import SplashScreen from '../screens/splashScreen';
import MainStack from '../routes/MainStack';
import MainNavigator from '../routes/MainNavigator';
const RootStack = createStackNavigator();
const App = () => {
const [isLoading, setIsLoading] = useState(true);
const [auth, setAuth] = useState(false);
useEffect(() => {
setTimeout(async () => {
setIsLoading(!isLoading);
setAuth(false);
}, 2000)
}, []);
return (
<Provider store={store}>
<NavigationContainer>
<StatusBar backgroundColor={isLoading ? '#000000' : '#F3F3F3'} />
{isLoading ?
<SplashScreen/>:
auth ?
<MainNavigator/>
:
<MainStack />
</NavigationContainer>
</Provider>
);
//}
}
export default App;
MainStack.js File
export default ({navigation}) => {
return (
<Stack.Navigator initialRouteName="Login" screenOptions={{
headerShown: false
}}>
<Stack.Screen name="Login" component={Login}/>
<Stack.Screen name="ForgotPassword" component={ForgetPassword}/>
<Stack.Screen name="VerifyOTP" component={VerifyOTP} />
<Stack.Screen name="ResetPassword" component={ResetPassword} />
</Stack.Navigator>
);
};
MainNavigator.js
const DashboardStackScreen = ({ navigation }) => {
return (
<DashboardStack.Navigator screenOptions={{
headerTitle: 'Good Morning, John',
header: props => <GradientHeader {...props} />,
headerLeft: () => (
<TouchableOpacity onPress={navigation.openDrawer} style={{padding: 20}}>
<Image source={require('../assets/images/menu_bar.png')} style={{width:18, height:12}}/>
</TouchableOpacity>
),
headerTransparent: true,
headerStyle: {
backgroundColor: 'transparent'
},
headerTintColor: '#fff',
headerTitleStyle: { fontFamily: 'OpenSans-SemiBold', fontSize: 20},
}}>
<DashboardStack.Screen name="Dashboard" component={Dashboard} />
</DashboardStack.Navigator>
);
}
export default({navigation}) =>{
return (
<Drawer.Navigator initialRouteName="Dashboard" drawerContent={(props) => <DrawerContent {...props} />} hideStatusBar={false} focused={true} labelStyle={{ fontSize: 14, fontFamily: 'OpenSans-SemiBold' }} drawerContentOptions={{ activeBackgroundColor: "#F1F1F1", activeTintColor: "#000000", inactiveTintColor: "#818181",itemStyle: { marginLeft:0, paddingHorizontal: 10,width:'100%', borderRadius: 0}}}
>
<Drawer.Screen name="Dashboard" component={DashboardStackScreen} options={{
drawerIcon: ({ focused, size }) => (
<Image source={require('../assets/images/dashboard.png')} style={{ height: 17.78, width: 16}} resizeMode="contain"/>
),
}}
/>
<Drawer.Screen name="My Profile" component={MyProfileStackScreen} options={{
drawerIcon: ({ focused, size }) => (
<Image source={require('../assets/images/profile.png')} style={{ height: 16, width: 16 }} resizeMode="contain"/>
),
}} />
<Drawer.Screen name="LMS" component={LmsStackScreen} options={{
drawerIcon: ({ focused, size }) => (
<Image source={require('../assets/images/lms.png')} style={{ height: 14.46, width: 16 }} resizeMode="contain"/>
),
}} />
<Drawer.Screen name="Change Password" component={ChangePasswordStackScreen} options={{
drawerIcon: ({ focused, size }) => (
<Image source={require('../assets/images/key.png')} style={{ height: 8.73, width: 16 }} resizeMode="contain"/>
),
}} />
</Drawer.Navigator>
);
};
Login.js
import React from 'react';
import { connect } from 'react-redux';
import LinearGradient from 'react-native-linear-gradient';
import { ScrollView, KeyboardAvoidingView, View, Text, Image, TextInput, TouchableOpacity, SafeAreaView, Keyboard, TouchableWithoutFeedback } from 'react-native';
import Toast from 'react-native-simple-toast';
/* import CheckBox from '@react-native-community/checkbox'; */
/* import CheckBox from 'react-native-check-box'; */
import Checkbox from 'react-native-custom-checkbox';
import styles from '../utility/styles';
import { thunk_login_creator } from '../actions/login';
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
color: "#ffffff",
isSelected: false,
isPasswordHidden: true,
employeeId: '',
employeeIdError: '',
password: '',
passwordError: ''
}
}
handleValidation(type) {
let obj = this.state;
switch (type) {
case 'employeeId':
obj.employeeIdError = this.state.employeeId.trim() == "" ? "Employee ID is required" : "";
break;
case 'password':
obj.passwordError = this.state.password.trim() == "" ? "Password is required" : ""
break;
default:
break;
}
this.setState({ obj });
}
handleChange(type, text) {
if (type == 'employeeId') {
this.setState({ employeeId: text.trim() }, () => {
this.handleValidation(type);
});
} else {
this.setState({ password: text.trim() }, () => {
this.handleValidation(type);
});
}
}
handlePasswordToggle = () => {
console.log('handlePasswordToggle called');
this.setState({ isPasswordHidden: !this.state.isPasswordHidden })
}
handleRememberMe = () => {
console.log('handleRememberMe called');
this.setState({ isSelected: !this.state.isSelected }, () => {
if (this.state.isSelected == true) {
this.setState({ color: "red" });
} else {
this.setState({ color: "black" });
}
console.log('remember me--->', this.state.isSelected);
})
}
handleLogin = () => {
console.log('handleLogin called')
this.props.dispatch(thunk_login_creator({ 'empId': this.state.employeeId, 'password': this.state.password, 'isRemembered': this.state.isSelected },/* ()=>{
if (this.props.data.login.loading) {
Toast.show('Loading...');
} else if (this.props.data.login.error) {
Toast.show('Invalid Credentials');
} else {
this.setState({
employeeId: '',
employeeIdError: '',
password: '',
passwordError: ''
}, () => {
this.props.navigation.navigate('Dashboard')
})
}
} */));
if (this.props.data.login.loading) {
Toast.show('Loading...');
} else if (this.props.data.login.error != '') {
Toast.show(this.props.data.login.error);
} else {
this.setState({
isSelected: false,
employeeId: '',
employeeIdError: '',
password: '',
passwordError: ''
}, () => {
//this.props.navigation.navigate('Dashboard')
})
}
}
render() {
return (
<SafeAreaView style={styles.baseContainer}>
<KeyboardAvoidingView behaviour="padding" style={styles.baseContainer} >
<TouchableWithoutFeedback style={styles.baseContainer} onPress={Keyboard.dismiss}>
<View style={styles.baseContainer} >
<View style={styles.ovalContainer}>
<View style={styles.oval1} />
<View style={styles.oval2} />
<View style={styles.logoContainer}>
<Image source={require('../assets/images/MJB_Logo.png')} style={styles.logo} />
</View>
</View>
<View style={styles.formContainer}>
<View style={styles.loginTextContainer}>
<Text style={styles.loginText}>Login</Text>
</View>
<View style={styles.loginFormContainer}>
<View>
<Text style={styles.labelText}>Employee ID</Text>
<TextInput style={styles.inputField} value={this.state.employeeId} placeholder="" onBlur={() => this.handleValidation('employeeId')} keyboardType="email-address" returnKeyType="next" autoCorrect={false} onSubmitEditing={() => this.refs.txtPassword.focus()} onChangeText={(text) => this.handleChange('employeeId', text)} />
<Text style={styles.errorText} >{this.state.employeeIdError}</Text>
</View>
<View>
<Text style={styles.labelText}>Password</Text>
<View>
<TextInput style={styles.inputField} value={this.state.password} onBlur={() => this.handleValidation('password')}
placeholder="" returnKeyType="go" secureTextEntry={this.state.isPasswordHidden} autoCorrect={false} ref={"txtPassword"} onChangeText={(text) => this.handleChange('password', text)} />
<TouchableWithoutFeedback onPress={this.handlePasswordToggle} style={styles.eyeIconContainer}>
<Image source={this.state.isPasswordHidden
? require('../assets/images/eye_on.png')
: require('../assets/images/eye_off.png')} style={styles.eyeIcon} resizeMode="contain" />
</TouchableWithoutFeedback>
</View>
<Text style={styles.errorText} >{this.state.passwordError}</Text>
</View>
{/*
<View style={styles.rememberMeContainer}>
<CheckBox
value={this.state.isSelected}
onValueChange={this.handleRememberMe}
style={styles.checkbox}
tintColors={{ true: '#6CCFF6', false: '#CED4DA' }}
onFillColor="#6CCFF6"
onTintColor="#6CCFF6"
/>
<Text style={styles.rememberMeText}>Remember Me</Text>
</View>
*/}
<View style={styles.rememberMeContainer}>
{/* <CheckBox
isChecked={this.state.isSelected}
onClick={this.handleRememberMe}
style={styles.checkbox}
checkBoxColor="#CED4DA"
checkedCheckBoxColor="#6CCFF6"
/> */}
<Checkbox
checked={this.state.isSelected}
style={{/* backgroundColor: (this.state.isSelected ? 'green':'black'), */ color: '#000000', borderRadius: 3, borderColor: '#CED4DA', borderWidth: 1 }}
size={25}
onChange={this.handleRememberMe} />
<Text style={styles.rememberMeText}>Remember Me</Text>
</View>
<TouchableOpacity disabled={this.state.employeeId == '' || this.state.password == '' ? true : false} onPress={this.handleLogin} >
<LinearGradient start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} colors={['#6CCFF6', '#596AB2']} style={styles.linearGradient}>
<Text style={styles.buttonText}>
Login
</Text>
</LinearGradient>
</TouchableOpacity>
<View style={styles.forgetPasswordTextContainer} >
<Text style={styles.forgetText}>Forgot password? <Text style={styles.resetText} onPress={() =>
this.props.navigation.navigate('ForgotPassword')
}> Reset</Text></Text>
</View>
</View>
{/* <View style={styles.forgetPasswordTextContainer}>
<Text style={styles.forgetText}>Forgot password? <Text style={styles.resetText} onPress={() =>
this.props.navigation.navigate('ForgotPassword')
}> Reset</Text></Text>
</View> */}
</View>
</View>
</TouchableWithoutFeedback >
</KeyboardAvoidingView>
</SafeAreaView>
)
}
};
const mapStateToProps = state => {
console.log('login state===>', state)
return {
data: state
};
};
export default connect(mapStateToProps)(Login);
Any help is appreciated.
解决方案
您是否使用令牌登录用户?通常,我使用 asyncStorage 将令牌存储在我的应用程序中。在您的登录功能中,您可以将用户设置为 redux 状态并将您的令牌设置为 asyncStorage。安装 app.js 时,您可以在显示初始屏幕时检查令牌是否存在。如果令牌在那里,你可以获取用户并设置你的 redux 状态,但如果没有,它会在启动屏幕之后返回登录页面。
因此,总的来说,在启动应用程序时,您的初始屏幕将检查 asyncStorage 中是否有令牌,以及您的 redux 状态中是否有登录用户。如果其中任何一个不存在,您将要求用户再次登录。或者,如果您有令牌,您可以尝试再次获取用户。
推荐阅读
- javascript - 将加载状态应用于数组中单击的项目 - React
- python - 诗歌安装pandas mac os失败
- amazon-web-services - Sagemaker 笔记本与 Sagemaker 容器
- memory - 使用 Verilog 验证单端口 RAM
- python - Even when giving proper shape for slicing I am getting a weird shape
- java - Niimbot printer and Android Studio
- javascript - Google Books Thumbail not working in Image React Native
- r - 当 R 中一个日期大于另一个日期时生成条件
- regex - PCRE正则表达式:排除单词的最后一部分
- javascript - 如何通过移动手机从各个角度查看手机链接上托管的 3D 图片?