React Native:如何用Mobx可观察数据设置常量并配置Axios BaseURL
Let's break down your two questions and work through practical, actionable solutions for each:
1. Using MobX Observable Data for "Constant" Values
First, a key clarification: JavaScript const values are static and set at initialization, but MobX observables are dynamic (they can update over time). You can’t directly assign a dynamic observable value to a const and expect it to reflect changes automatically. Instead, here’s the right approach:
Step 1: Create a MobX Config Store
Build a dedicated store to hold your app’s dynamic configuration values:
import { makeAutoObservable } from "mobx"; class ConfigStore { // Initialize with a placeholder value (can be updated later) baseUrl = ""; constructor() { makeAutoObservable(this); // Enables reactivity for all properties } // Method to update the base URL when needed setBaseUrl(newUrl) { this.baseUrl = newUrl; } } // Export a singleton instance of the store (only one instance exists) export const configStore = new ConfigStore();
Step 2: Access the Observable Value Where Needed
Instead of exporting a fixed const BASE_URL, use the store’s observable property directly wherever you need it. For example:
// In your API file or components import { configStore } from "./path/to/ConfigStore"; // This will always return the latest value from the MobX store const currentBaseUrl = configStore.baseUrl;
If you want a helper function to mimic a "constant" getter (while still being dynamic), you can add:
export const getBaseUrl = () => configStore.baseUrl;
Just remember: this returns the latest observable value, not a static constant.
2. Replacing Axios Base URL with Secure Data from Native Module
Your original AsyncStorage approach fails because AsyncStorage.getItem() is asynchronous—you can’t use it directly in the synchronous axios.create() call. Here are two reliable fixes:
Option 1: Initialize Axios After Fetching Secure Data
We’ll create the Axios instance only after retrieving the base URL from the Native module (and cache it in AsyncStorage for future app launches):
Step 1: Wrap the Native Module Call in a Promise
Convert the callback-based ApiKeys.getApiKeys to a Promise for easier async/await handling:
import AsyncStorage from "@react-native-async-storage/async-storage"; const getApiKeys = () => { return new Promise((resolve, reject) => { ApiKeys.getApiKeys( (data) => { try { const secureData = JSON.parse(data); resolve(secureData); } catch (parseError) { reject(parseError); } }, (apiError) => reject(apiError) ); }); };
Step 2: Lazy-Initialize the Axios Instance
import axios from "axios"; let apiInstance; // Initialize the API with the correct base URL export const initApi = async () => { try { // Check AsyncStorage first to avoid re-calling the Native module let cachedKeys = await AsyncStorage.getItem(SECURE_KEY); let secureData; if (!cachedKeys) { // Fetch from Native module if no cache exists secureData = await getApiKeys(); await AsyncStorage.setItem(SECURE_KEY, JSON.stringify(secureData)); } else { secureData = JSON.parse(cachedKeys); } // Create the Axios instance with the correct base URL apiInstance = axios.create({ baseURL: secureData.SOHOJ_APP_API_BASE_URL_DEVELOPMENT, timeout: 10 * 1000, headers: { "content-type": "application/json" }, }); } catch (error) { console.error("Failed to initialize API:", error); // Optional: Set a fallback base URL if something goes wrong apiInstance = axios.create({ baseURL: "https://your-fallback-url.com", timeout: 10 * 1000, headers: { "content-type": "application/json" }, }); } }; // Export a function to get the initialized API instance export const getApi = () => { if (!apiInstance) { throw new Error("API not initialized! Call initApi() first (e.g., in App.js)."); } return apiInstance; };
Step 3: Initialize the API in Your App’s Entry Point
In App.js, call initApi() when your app starts, and show a loading state until it’s ready:
import { useEffect, useState } from "react"; import { View, Text } from "react-native"; import { initApi } from "./path/to/api.js"; const App = () => { const [isApiReady, setIsApiReady] = useState(false); useEffect(() => { const setup = async () => { await initApi(); setIsApiReady(true); }; setup(); }, []); if (!isApiReady) { return ( <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}> <Text>Loading...</Text> </View> ); } // Render your main app once the API is ready return ( // Your app components here ); }; export default App;
Step 4: Use the API Instance for Requests
import { getApi } from "./path/to/api.js"; const makeRequest = async () => { try { const api = getApi(); const response = await api.post("api_end_point", parameters, headers); // Handle successful response } catch (error) { // Handle error } };
Option 2: Update Axios Defaults After Fetching Data
If you prefer to create the Axios instance upfront and update the base URL later:
import axios from "axios"; // Create a base Axios instance with temporary values export const api = axios.create({ baseURL: "", // Empty for now timeout: 10 * 1000, headers: { "content-type": "application/json" }, }); // Add a request interceptor to prevent calls before base URL is set api.interceptors.request.use( (config) => { if (!config.baseURL) { throw new Error("API base URL not configured yet."); } return config; }, (error) => Promise.reject(error) ); // Setup function to update the base URL export const setupApiBaseUrl = async () => { try { let secureData = await AsyncStorage.getItem(SECURE_KEY); if (!secureData) { secureData = await getApiKeys(); await AsyncStorage.setItem(SECURE_KEY, JSON.stringify(secureData)); } else { secureData = JSON.parse(secureData); } // Update Axios's default base URL api.defaults.baseURL = secureData.SOHOJ_APP_API_BASE_URL_DEVELOPMENT; } catch (error) { console.error("Failed to setup API base URL:", error); api.defaults.baseURL = "https://your-fallback-url.com"; } };
Call setupApiBaseUrl() in App.js’s useEffect like before, and use api.post(...) directly in your code.
内容的提问来源于stack exchange,提问作者Wasi Sadman

