React Native导航初始页面异常排查:完成引导页后仍跳转至引导页而非登录页
Let’s break down what’s going wrong and how to fix it:
1. Reducers Shouldn’t Handle Async Side Effects
Your current reducer is calling AsyncStorage.setItem directly, which is an asynchronous operation. Redux reducers must be pure, synchronous functions—side effects like async storage calls belong in thunks, not reducers. This is likely causing inconsistent state persistence.
Fix: Use an Async Thunk
Replace your onboarding slice with this, which uses a Redux Toolkit thunk to handle the async storage:
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit' import AsyncStorage from '@react-native-async-storage/async-storage' export interface onboardingState { viewedOnBoarding: boolean } const initialState: onboardingState = { viewedOnBoarding: false, } // Async thunk to handle state update and storage export const setOnboardingAsync = createAsyncThunk( 'onboarding/setOnboardingAsync', async (value: boolean) => { await AsyncStorage.setItem('@onBoarding', JSON.stringify(value)) return value } ) export const onboardingSlice = createSlice({ name: 'onboarding', initialState, reducers: {}, extraReducers: (builder) => { builder.addCase(setOnboardingAsync.fulfilled, (state, action) => { state.viewedOnBoarding = action.payload }) }, }) export default onboardingSlice.reducer
2. Incorrect AsyncStorage Value Parsing
Your checkOnboarding function was checking if the stored value exists (non-null) but not parsing it back to a boolean. If the stored value was "false" (a string), it would still set onboarded to true—which is wrong.
Fix: Parse the Stored JSON
Update your checkOnboarding function to parse the stored value:
const checkOnboarding = async () => { try { const value = await AsyncStorage.getItem('@onBoarding') // Parse the string back to a boolean const parsedValue = value ? JSON.parse(value) : false setOnboarded(parsedValue) } catch (err) { console.error('Error checking onboarding status:', err) } finally { setIsLoading(false) } }
3. InitialRouteName Only Runs Once
The initialRouteName prop is only evaluated when the navigator first mounts. Even if onboarded updates later, the navigator won’t re-render with the new route. You need to wait for the AsyncStorage check to complete before rendering the navigator.
Fix: Add a Loading State
Show a loading screen while checking the onboarding status, then render the navigator with the correct initial route:
function RootStack() { const Stack = createNativeStackNavigator<RootStackParamList>() const [isLoading, setIsLoading] = React.useState(true) const [onboarded, setOnboarded] = React.useState(false) const checkOnboarding = async () => { try { const value = await AsyncStorage.getItem('@onBoarding') const parsedValue = value ? JSON.parse(value) : false setOnboarded(parsedValue) } catch (err) { console.error('Error checking onboarding status:', err) } finally { setIsLoading(false) } } useEffect(() => { checkOnboarding() }, []) // Show loading while we fetch the onboarding status if (isLoading) { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <ActivityIndicator size="large" color={theme.colors.primary} /> </View> ) } return ( <NavigationContainer> <Stack.Navigator initialRouteName={onboarded ? 'Login' : 'OnboardingScreen'} screenOptions={{ headerShown: false }} > <Stack.Screen name='Login' component={Login} /> <Stack.Screen name='OnboardingScreen' component={OnboardingScreen} /> <Stack.Screen name='Tabs' component={Tabs} /> <Stack.Screen name='DrawerNav' component={DrawerNav} /> <Stack.Screen name='OtpScreen' component={OtpScreen} options={{ title: 'OTP', headerShown: true, headerStyle: { backgroundColor: theme.colors.whiteSmoke }, }} /> </Stack.Navigator> </NavigationContainer> ) }
4. Clean Up the onSkip Handler
Remove the redundant AsyncStorage.setItem call in your skip button—your thunk now handles this:
onSkip={() => { dispatch(setOnboardingAsync(true)) navigation.replace('Login') }}
These changes should ensure your app correctly persists the onboarding status and navigates to the right screen on startup.
内容的提问来源于stack exchange,提问作者Mohamed Abdinasir

