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

React Native:如何在TS文件的API异常中处理导航跳转?求更优方案

问题描述

我们使用Expo和@react-navigation/native-stack开发应用,要求用户必须接受最新服务条款才能使用。若用户登录期间条款发生变更,需要弹出提示并将其重定向至条款页面。

API调用位于独立的.ts文件中,无法自行处理导航。目前我们的方案是在根导航器中暴露一个函数供API文件调用,以此集中处理错误,避免在多个API调用位置分散处理逻辑。

理想的API调用逻辑如下:

// api.ts
export const doSomething = async () => {
    const response = await axios.post(
        'someUrl',
        { someData: 'someData' },
    ).catch((err) => {
        if (err.status === 401) {
            showAlertToNavigateToTermsOfUse();
        }
    })
}

当前实现方式是在根导航器中暴露一个对象:

export const exposedObject = {showAlertToNavigateToTermsOfUse: () => {}}

export default function RootNavigator() {
    const navigator = useNavigation<any>();

    exposedObject.showAlertToNavigateToTermsOfUse = () => {
        Alert.alert('服务条款变更', '服务条款已更新,请重新接受后继续使用')
        navigator.navigate(ScreenNames.TERMS_OF_USE_SCREEN);
    };

    // ... 导航器其余代码
}

该方案可行但不够优雅,想知道React Native有没有更优的实现方式?

更优实现方案

方案一:React Context封装导航服务

通过Context创建全局可用的导航服务,既保持API层与组件的解耦,又符合React的设计范式。

  1. 创建导航Context与自定义Hook
// NavigationService.tsx
import { createContext, useContext, useNavigation } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { ScreenNames } from './ScreenNames';
import { Alert } from 'react-native';

type NavigationServiceType = {
    showAlertAndNavigateToTerms: () => void;
};

const NavigationServiceContext = createContext<NavigationServiceType | undefined>(undefined);

export const NavigationServiceProvider = ({ children }: { children: React.ReactNode }) => {
    const navigation = useNavigation<NativeStackNavigationProp<any>>();

    const showAlertAndNavigateToTerms = () => {
        Alert.alert('服务条款变更', '服务条款已更新,请重新接受后继续使用');
        navigation.navigate(ScreenNames.TERMS_OF_USE_SCREEN);
    };

    return (
        <NavigationServiceContext.Provider value={{ showAlertAndNavigateToTerms }}>
            {children}
        </NavigationServiceContext.Provider>
    );
};

export const useNavigationService = () => {
    const context = useContext(NavigationServiceContext);
    if (!context) {
        throw new Error('useNavigationService必须在NavigationServiceProvider内部使用');
    }
    return context;
};
  1. 在根组件中包裹Provider
// App.tsx
import { NavigationServiceProvider } from './NavigationService';
import RootNavigator from './RootNavigator';

export default function App() {
    return (
        <NavigationServiceProvider>
            <RootNavigator />
        </NavigationServiceProvider>
    );
}
  1. 封装API为Hook供组件调用
// useApi.ts
import { useNavigationService } from './NavigationService';
import axios from 'axios';

export const useApi = () => {
    const { showAlertAndNavigateToTerms } = useNavigationService();

    const doSomething = async () => {
        try {
            const response = await axios.post('someUrl', { someData: 'someData' });
            return response;
        } catch (err) {
            if (err.response?.status === 401) {
                showAlertAndNavigateToTerms();
            }
            throw err;
        }
    };

    return { doSomething };
};

组件内使用示例:

// SomeComponent.tsx
import { useApi } from './useApi';

const SomeComponent = () => {
    const { doSomething } = useApi();

    const handlePress = async () => {
        await doSomething();
    };

    return <Button title="执行操作" onPress={handlePress} />;
};

方案二:事件总线解耦API与导航

通过事件发射器实现完全解耦,API层仅负责触发事件,导航层监听事件并处理逻辑。

  1. 创建事件总线
// EventBus.ts
import { EventEmitter } from 'events';

const eventBus = new EventEmitter();

export const EVENT_TERMS_UPDATED = 'TERMS_UPDATED';

export default eventBus;
  1. 在根导航器中监听事件
// RootNavigator.tsx
import { useEffect } from 'react';
import { useNavigation } from '@react-navigation/native';
import eventBus, { EVENT_TERMS_UPDATED } from './EventBus';
import { Alert } from 'react-native';
import { ScreenNames } from './ScreenNames';

export default function RootNavigator() {
    const navigator = useNavigation<any>();

    useEffect(() => {
        const handleTermsUpdated = () => {
            Alert.alert('服务条款变更', '服务条款已更新,请重新接受后继续使用');
            navigator.navigate(ScreenNames.TERMS_OF_USE_SCREEN);
        };

        eventBus.on(EVENT_TERMS_UPDATED, handleTermsUpdated);

        return () => {
            eventBus.off(EVENT_TERMS_UPDATED, handleTermsUpdated);
        };
    }, [navigator]);

    // ... 导航器其余代码
}
  1. 在API文件中触发事件
// api.ts
import axios from 'axios';
import eventBus, { EVENT_TERMS_UPDATED } from './EventBus';

export const doSomething = async () => {
    try {
        const response = await axios.post('someUrl', { someData: 'someData' });
        return response;
    } catch (err) {
        if (err.response?.status === 401) {
            eventBus.emit(EVENT_TERMS_UPDATED);
        }
        throw err;
    }
};

方案三:Axios拦截器+全局导航引用

适合需要全局统一处理API错误的场景,代码简洁且无需分散处理逻辑。

  1. 创建全局导航引用
// NavigationRef.ts
import type { NavigationContainerRef } from '@react-navigation/native';

export let navigationRef: NavigationContainerRef<any>;

export const setNavigationRef = (ref: NavigationContainerRef<any>) => {
    navigationRef = ref;
};
  1. 在根组件中设置引用
// App.tsx
import { NavigationContainer } from '@react-navigation/native';
import { setNavigationRef } from './NavigationRef';
import RootNavigator from './RootNavigator';

export default function App() {
    return (
        <NavigationContainer ref={setNavigationRef}>
            <RootNavigator />
        </NavigationContainer>
    );
}
  1. 配置Axios全局拦截器
// api.ts
import axios from 'axios';
import { navigationRef } from './NavigationRef';
import { Alert } from 'react-native';
import { ScreenNames } from './ScreenNames';

const api = axios.create({
    baseURL: 'your-base-url',
});

api.interceptors.response.use(
    (response) => response,
    (error) => {
        if (error.response?.status === 401) {
            Alert.alert('服务条款变更', '服务条款已更新,请重新接受后继续使用');
            navigationRef.navigate(ScreenNames.TERMS_OF_USE_SCREEN);
        }
        return Promise.reject(error);
    }
);

export const doSomething = async () => {
    const response = await api.post('someUrl', { someData: 'someData' });
    return response;
};
总结
  • 若偏好React组件范式,**方案一(Context+Hook)**是最贴合React设计理念的选择;
  • 若需要完全解耦API与导航层,**方案二(事件总线)**扩展性更强;
  • 若追求简洁的全局错误处理,**方案三(Axios拦截器+全局导航引用)**更高效。

内容的提问来源于stack exchange,提问作者Scott Fletcher

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 02:07:01