React Native登录异步函数执行顺序问题及加载状态实现
解决React Native登录异步流程提前触发SIGN_IN的问题
核心思路
先启动全局加载状态,等待登录请求、用户信息保存、页面配置获取所有异步操作完成后,再触发SIGN_IN动作;无论成功失败,最终关闭加载状态,避免首页提前加载。
1. 新增加载状态管理
在Redux的Auth模块中添加isLoading状态,用于控制加载页面的显示:
// authReducer.js const initialState = { isAuthenticated: false, user: null, isLoading: false, // 新增加载状态 loginError: null }; const authReducer = (state = initialState, action) => { switch (action.type) { case 'SET_LOADING': return { ...state, isLoading: action.payload }; case 'SIGN_IN': return { ...state, isAuthenticated: true, user: action.payload, loginError: null }; case 'LOGIN_FAILED': return { ...state, loginError: action.payload }; default: return state; } }; export default authReducer;
2. 重构登录异步流程
调整登录Action,确保所有异步操作完成后再触发SIGN_IN:
// authActions.js export const signIn = (credentials) => async (dispatch) => { // 开启加载状态 dispatch({ type: 'SET_LOADING', payload: true }); try { // 1. 执行登录请求 const loginResponse = await loginRequest(credentials); // 2. 执行后续异步操作:无依赖则用Promise.all并行提升性能 await Promise.all([ saveUserInfo(loginResponse.data), getAppScreensFromAPI(loginResponse.data.token) // 若需token则传入 ]); // 3. 所有异步操作完成后,触发登录成功 dispatch({ type: 'SIGN_IN', payload: loginResponse.data }); } catch (error) { // 登录失败处理 dispatch({ type: 'LOGIN_FAILED', payload: error.message }); // 可添加弹窗提示:Alert.alert('登录失败', error.message) } finally { // 无论成功失败,关闭加载状态 dispatch({ type: 'SET_LOADING', payload: false }); } };
注意:如果
saveUserInfo和getAppScreensFromAPI有依赖(比如后者需要前者保存的token),则改为串行执行:await saveUserInfo(loginResponse.data); await getAppScreensFromAPI(); // 从本地存储取token
3. 在登录组件中显示加载页面
通过Redux获取isLoading状态,渲染加载页面或登录表单:
// LoginScreen.js import { View, Text, TextInput, Button, ActivityIndicator, StyleSheet } from 'react-native'; import { useDispatch, useSelector } from 'react-redux'; import { signIn } from '../redux/actions/authActions'; import { useNavigation } from '@react-navigation/native'; const LoginScreen = () => { const dispatch = useDispatch(); const navigation = useNavigation(); const { isLoading, isAuthenticated, loginError } = useSelector(state => state.auth); // 监听登录成功状态,跳转首页 useEffect(() => { if (isAuthenticated) { navigation.replace('Home'); } }, [isAuthenticated, navigation]); const handleLogin = () => { const credentials = { username: 'test', password: '123456' }; // 替换为实际输入值 dispatch(signIn(credentials)); }; // 加载中显示加载页面 if (isLoading) { return ( <View style={styles.loadingContainer}> <ActivityIndicator size="large" color="#2196F3" /> <Text style={styles.loadingText}>登录中...</Text> </View> ); } return ( <View style={styles.container}> {loginError && <Text style={styles.errorText}>{loginError}</Text>} <TextInput style={styles.input} placeholder="用户名" /> <TextInput style={styles.input} placeholder="密码" secureTextEntry /> <Button title="登录" onPress={handleLogin} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', padding: 20 }, input: { borderWidth: 1, borderColor: '#ccc', padding: 10, marginBottom: 15, borderRadius: 5 }, errorText: { color: 'red', marginBottom: 15, textAlign: 'center' }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center' }, loadingText: { marginTop: 10, fontSize: 16 } }); export default LoginScreen;
内容的提问来源于stack exchange,提问作者svenfulen
相关产品推荐
相关产品推荐

