Expo React Native 52 SDK下Android端AsyncStorage Hook调用顺序错误求助
Expo React Native 52 SDK下AsyncStorage/Secure Store引发Android端Hook调用顺序错误问题
在基于Expo React Native 52 SDK的项目中,使用@react-native-async-storage/async-storage时遇到异常:Web端运行正常,但Android端触发Hook调用顺序错误。更换为Secure Store包后,同样出现该问题。
错误信息
Warning: React has detected a change in the order of Hooks called by MainLayout. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://react.dev/link/rules-of-hooks Previous render Next render ------------------------------------------------------ 1. useState useState 2. useEffect useEffect 3. useContext useContext 4. useSyncExternalStore useMemo ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ in MainLayout in Unknown (created by Route((main))) in Suspense (created by Route((main))) in Route (created by Route((main))) in Route((main)) (created by SceneView) in StaticContainer in EnsureSingleNavigator (created by SceneView) in SceneView (created by SlotNavigator) in PreventRemoveProvider (created by NavigationContent) in NavigationContent in Unknown (created by SlotNavigator) in SlotNavigator (created by Slot) in Slot (created by AuthRoutes) in AuthRoutes (created by RootLayout) in PortalProviderComponent (created by BottomSheetModalProviderWrapper) in BottomSheetModalProviderWrapper (created by RootLayout) in RNGestureHandlerRootView (created by GestureHandlerRootView) in GestureHandlerRootView (created by RootLayout) in BottomSheetContextProvider (created by RootLayout) in ApplicationContextProvider (created by RootLayout) in AuthContextProvider (created by RootLayout) in RNGestureHandlerRootView (created by GestureHandlerRootView) in GestureHandlerRootView (created by RootLayout) in ThemeContextProvider (created by RootLayout) in RootLayout in Unknown (created by Route()) in Suspense (created by Route()) in Route (created by Route()) in Route() (created by ContextNavigator) in RNCSafeAreaProvider (created by SafeAreaProvider) in SafeAreaProvider (created by wrapper) in wrapper (created by ContextNavigator) in ThemeProvider in EnsureSingleNavigator in BaseNavigationContainer in NavigationContainerInner (created by ContextNavigator) in ContextNavigator (created by ExpoRoot) in ExpoRoot (created by App) in App (created by ErrorOverlay) in ErrorToastContainer (created by ErrorOverlay) in ErrorOverlay (created by withDevTools(ErrorOverlay)) in withDevTools(ErrorOverlay) in RCTView (created by View) in View (created by AppContainer) in RCTView (created by View) in View (created by AppContainer) in AppContainer in main(RootComponent)
库使用示例代码
import AsyncStorage from "@react-native-async-storage/async-storage"; import { useRouter } from "expo-router"; import { useEffect, useState } from "react"; import Toast from "react-native-toast-message"; import { login as apiLogin, logout as apiLogout, verify as apiVerify } from "../api"; export const useAuth = () => { const [isAuth, setIsAuth] = useState<boolean | "pending">("pending"); const [token, setToken] = useState<string | null>(null); const router = useRouter(); const login = async (data: { phone: string; password: string }, rememberMe: boolean) => { try { const res = await apiLogin(data); await AsyncStorage.setItem("token", res.result.token); await AsyncStorage.setItem("refresh_token", res.result.refresh_token); setIsAuth(true); setToken(res.result.token); router.replace("/info"); } catch (e) { Toast.show({ type: "error", text1: e as string, }); } }; const logout = async () => { try { const token = (await AsyncStorage.getItem("token")) as string; await apiLogout(token); } catch (e) { Toast.show({ type: "error", text1: e as string, }); } await AsyncStorage.removeItem("token"); await AsyncStorage.removeItem("refresh_token"); setIsAuth(false); setToken(null); router.replace("/auth"); }; const verify = async () => { try { const token = await AsyncStorage.getItem("token"); if (token) { const res = await apiVerify(token); if (res.ok && res.result) { setToken(token); return setIsAuth(true); } else { setToken(null); return await logout(); } } await logout(); } catch (e) { await logout(); Toast.show({ type: "error", text1: e as string, }); } }; useEffect(() => { const effectWrapper = async () => { const token = await AsyncStorage.getItem("token"); if (token !== null) { verify(); } else { setIsAuth(false); } }; effectWrapper(); }, []); return { login, logout, verify, setIsAuth, isAuth, token }; };
已尝试的解决方案
- 更换不同的存储库
- 将Expo SDK降级到51版本
- 在干净的新项目中调用存储库函数
- 仅在useEffect、useCallback或事件处理器中使用存储库API
以上方案均未解决问题,寻求帮助。
内容的提问来源于stack exchange,提问作者Даниил Дуров
相关产品推荐
相关产品推荐

