Вопрос или проблема
Я новичок в React Native, и у меня возникла проблема, когда я пытаюсь получить токен пользователя, чтобы проверить, аутентифицирован ли пользователь. Если токен найден, я считаю пользователя аутентифицированным и пытаюсь отобразить корневой стек. Однако проблема, с которой я сталкиваюсь, заключается в том, что экраны из AuthStack по-прежнему монтируются поверх RootStack. Можете, пожалуйста, помочь мне?
Ниже представлен мой код:
import { router, Slot, Stack, Tabs } from "expo-router";
import { StatusBar } from "expo-status-bar";
import { useContext, useEffect, useState } from "react";
import { AuthContext, AuthContextProvider } from "../context/AuthContext";
import { ActivityIndicator, View } from "react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { getUserInforWithToken } from "../controller/AuthController";
function RootStack() {
return (
<Stack>
<Stack.Screen name="(root)/index" options={{ title: "Добро пожаловать" }} />
</Stack>
);
}
function AuthStack() {
return (
<Stack
screenOptions={{
headerStyle: { backgroundColor: "#6779ef" },
headerTintColor: "white",
contentStyle: { backgroundColor: "#6779ef" },
headerShown: false,
}}>
<Stack.Screen name="(auth)/index" options={{ title: "Страница входа" }} />
<Stack.Screen name="(auth)/signup" options={{ title: "Страница регистрации" }} />
</Stack>
);
}
function Root() {
const { setIsLogin, setToken, setIsTryingLogin, isTryingLogin } =
useContext(AuthContext);
const [isLoading, setIsLoading] = useState(true);
const { isLogin } = useContext(AuthContext);
useEffect(() => {
const checkAuth = async () => {
const token = await AsyncStorage.getItem("token");
if (token) {
console.log("токен найден, пользователь вошел в систему");
setIsLogin(true);
setToken(token);
} else {
setIsLogin(false);
}
// Установить попытку входа в систему в false после проверки токена
setIsTryingLogin(false);
};
checkAuth();
}, []); // Это выполняется только один раз при монтировании компонента
useEffect(() => {
if (!isTryingLogin) {
setIsLoading(false);
}
}, [isTryingLogin]);
if (isLoading) {
console.log("Загрузка...");
return (
<View className="flex-1 justify-center items-center">
<ActivityIndicator className="p-2 m-2" animating size="large" />
</View>
);
}
return isLogin ? <RootStack /> : <AuthStack />;
}
export default function Layout() {
return (
<AuthContextProvider>
<StatusBar style="light" />
<Root />
</AuthContextProvider>
);
}
Я пробовал Chatgpt и читал документы, а также пытался следовать за кем-то на YouTube, но ничего не сработало.
Ответ или решение
На первый взгляд, ваша проблема заключается в том, что вы не контролируете восприятие стека навигации между AuthStack
и RootStack
должным образом. Вероятно, проблема заключается в том, что AuthStack
и RootStack
оба рендерятся одновременно, поэтому экраны из AuthStack
накладываются на экраны из RootStack
.
Есть несколько вещей, которые вы можете попробовать, чтобы решить эту проблему:
-
Правильный контроль состояния аутентификации: Убедитесь, что состояние аутентификации корректно обрабатывается в компоненте
Root
. Ваш код в целом выглядит хорошо, однако контроль за состояниемisLogin
и условия рендеринга могут быть улучшены. -
Структура навигации: Вместо того, чтобы возвращать
AuthStack
илиRootStack
в зависимости от состояния аутентификации, вы можете использовать условную логику внутри одного стека, чтобы контролировать навигацию более эффективно.
Вот пример, как вы можете изменить ваш компонент Root
, чтобы избежать накладок экранов:
function Root() {
const { setIsLogin, setToken, setIsTryingLogin, isTryingLogin } = useContext(AuthContext);
const [isLoading, setIsLoading] = useState(true);
const { isLogin } = useContext(AuthContext);
useEffect(() => {
const checkAuth = async () => {
const token = await AsyncStorage.getItem("token");
if (token) {
console.log("token found, user logged in");
setIsLogin(true);
setToken(token);
} else {
setIsLogin(false);
}
setIsTryingLogin(false);
};
checkAuth();
}, []);
useEffect(() => {
if (!isTryingLogin) {
setIsLoading(false);
}
}, [isTryingLogin]);
if (isLoading) {
console.log("Loading...");
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<ActivityIndicator style={{ padding: 2, margin: 2 }} animating size="large" />
</View>
);
}
// Основное изменение здесь
return (
<Stack>
{isLogin ? <RootStack /> : <AuthStack />}
</Stack>
);
}
-
Использование React Navigation: Если вы еще не используете его, возможно, вам стоит рассмотреть возможность использования пакета
@react-navigation/native
, который предоставляет более удобные инструменты для работы с навигацией в приложении React Native. Это может облегчить создание и управление стеками навигации. -
Тестирование переходов: Убедитесь, что ваше состояние аутентификации действительно меняется в ожидаемые моменты. Вы можете добавить консольные логирования для
isLogin
и состояния загрузки, чтобы убедиться, что логика работает так, как задумано.
Эти изменения должны помочь предотвратить наложение экранов аутентификации и корневого стека навигации. Если проблема сохраняется, стоит проверить, возможно ли наличие дополнительных факторов, таких как ошибки в других частях вашего кода.