You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,提问作者Даниил Дуров

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 02:52:33