Expo Supabase无效刷新令牌引发React Native应用白屏崩溃求助
React Native/Expo/Supabase应用白屏问题:无效刷新令牌错误引发崩溃
我用React Native、Expo和Supabase开发办公应用,当前遇到的核心问题是:会话销毁时会触发AuthApiError: Invalid Refresh Token: Refresh Token Not Found错误,错误本身不重要,但触发时应用会直接白屏崩溃,目前还没找到崩溃原因。
复现步骤
- 在电脑模拟器登录应用后关闭
- 在手机端登录同一账号
- 在手机端登出(销毁会话)
- 等待约2小时
- 重新打开模拟器中的应用,即可触发错误;反向操作的话,手机端会出现白屏
我尝试过用try/catch捕获错误、检测到无会话时跳转登录页,但都没能阻止应用崩溃,推测是错误未被正确处理导致的崩溃。
初始代码
import { supabase } from "@/lib/supabase"; import { AuthApiError, Session } from "@supabase/supabase-js"; import { router } from "expo-router"; import { PropsWithChildren, createContext, useContext, useEffect, useState } from "react"; type AuthData = { session: Session | null; profile: any; loading: boolean; isAdmin: boolean; }; const AuthContext = createContext<AuthData>({ session: null, profile: null, loading: true, isAdmin: false, }); export default function AuthProvider({ children }: PropsWithChildren) { const [newSession, setNewSession] = useState<Session | null>(null); const [profile, setProfile] = useState<any>(null); const [loading, setLoading] = useState(true); useEffect(() => { supabase.auth.getSession().then(({ data: { session } }) => { setNewSession(session); if (session) { // Fetch profile when a session exists supabase .from('profiles') .select('*') .eq('id', session.user.id) .single() .then(({ data }) => { setProfile(data || null); setLoading(false); }) } else { console.log('No session found'); setLoading(false); router.push('/(auth)/sign-in'); } }); supabase.auth.onAuthStateChange(async (_event, session) => { setNewSession(session); if (session) { // fetch profile const { data } = await supabase .from('profiles') .select('*') .eq('id', session.user.id) .single(); setProfile(data || null); } }); }, []); return <AuthContext.Provider value={{session: newSession, loading, profile, isAdmin: profile?.role === 'ADMIN'}}>{children}</AuthContext.Provider>; } export const useAuth = () => useContext(AuthContext);
Edit 1:补充完整代码(原仅提供useEffect部分)
Edit 2:参考建议修改后的代码(操作可能有误)
useEffect(() => { // Set up the listener for auth state changes const { data: { subscription } } = supabase.auth.onAuthStateChange( (event, session) => { if (event === 'SIGNED_OUT') { // Handle sign out by setting session to null setNewSession(null); router.push('/(auth)/sign-in'); setLoading(false); } else if (session) { // Update the session state if a session is present setNewSession(session); setLoading(false); } else { // This handles the case when there is no session (e.g., the user is not logged in) setNewSession(null); router.push('/(auth)/sign-in'); setLoading(false); } } ); // Clean up the subscription on component unmount return () => { subscription.unsubscribe(); }; }, [router]); useEffect(() => { if (newSession) { console.log('session found'); const fetchProfile = async () => { const { data } = await supabase .from('profiles') .select('*') .eq('id', newSession.user.id) .single(); setProfile(data || null); } fetchProfile(); // Call the async function } else { console.log(' No session found'); } }, [newSession]);
Edit 3:进一步调整后的代码
export default function AuthProvider({ children }: PropsWithChildren) { const [newSession, setNewSession] = useState<Session | null>(null); const [profile, setProfile] = useState<any>(null); const [loading, setLoading] = useState(true); useEffect(() => { // Set up the listener for auth state changes const { data: { subscription } } = supabase.auth.onAuthStateChange( (event, session) => { if (event === 'SIGNED_OUT') { // Handle sign out by setting session to null setNewSession(null); //router.push('/(auth)/sign-in'); setLoading(false); } else if (session) { // Update the session state if a session is present setNewSession(session); setLoading(false); console.log(session); // fetch profile console.log('session found'); setTimeout(async () => { const { data } = await supabase .from('profiles') .select('*') .eq('id', session.user.id) .single(); setProfile(data || null); }, 0); } else { // This handles the case when there is no session (e.g., the user is not logged in) setNewSession(null); //router.push('/(auth)/sign-in'); setLoading(false); } } ); // Clean up the subscription on component unmount return () => { subscription.unsubscribe(); }; }, []); return <AuthContext.Provider value={{session: newSession, loading, profile, isAdmin: profile?.role === 'ADMIN'}}>{children}</AuthContext.Provider>; } export const useAuth = () => useContext(AuthContext);
内容的提问来源于stack exchange,提问作者ItsaMeEr
相关产品推荐
相关产品推荐

