native-data-fetching

安装量: 13.6K
排名: #155

安装

npx skills add https://github.com/expo/skills --skill native-data-fetching

Expo Networking You MUST use this skill for ANY networking work including API requests, data fetching, caching, or network debugging. References Consult these resources as needed: references/ expo-router-loaders.md Route-level data loading with Expo Router loaders (web, SDK 55+) When to Use Use this skill when: Implementing API requests Setting up data fetching (React Query, SWR) Using Expo Router data loaders ( useLoaderData , web SDK 55+) Debugging network failures Implementing caching strategies Handling offline scenarios Authentication/token management Configuring API URLs and environment variables Preferences Avoid axios, prefer expo/fetch Common Issues & Solutions 1. Basic Fetch Usage Simple GET request : const fetchUser = async ( userId : string ) => { const response = await fetch ( https://api.example.com/users/ ${ userId } ) ; if ( ! response . ok ) { throw new Error ( HTTP error! status: ${ response . status } ) ; } return response . json ( ) ; } ; POST request with body : const createUser = async ( userData : UserData ) => { const response = await fetch ( "https://api.example.com/users" , { method : "POST" , headers : { "Content-Type" : "application/json" , Authorization : Bearer ${ token } , } , body : JSON . stringify ( userData ) , } ) ; if ( ! response . ok ) { const error = await response . json ( ) ; throw new Error ( error . message ) ; } return response . json ( ) ; } ; 2. React Query (TanStack Query) Setup : // app/_layout.tsx import { QueryClient , QueryClientProvider } from "@tanstack/react-query" ; const queryClient = new QueryClient ( { defaultOptions : { queries : { staleTime : 1000 * 60 * 5 , // 5 minutes retry : 2 , } , } , } ) ; export default function RootLayout ( ) { return ( < QueryClientProvider client = { queryClient }

< Stack /> </ QueryClientProvider

) ; } Fetching data : import { useQuery } from "@tanstack/react-query" ; function UserProfile ( { userId } : { userId : string } ) { const { data , isLoading , error , refetch } = useQuery ( { queryKey : [ "user" , userId ] , queryFn : ( ) => fetchUser ( userId ) , } ) ; if ( isLoading ) return < Loading /> ; if ( error ) return < Error message = { error . message } /> ; return < Profile user = { data } /> ; } Mutations : import { useMutation , useQueryClient } from "@tanstack/react-query" ; function CreateUserForm ( ) { const queryClient = useQueryClient ( ) ; const mutation = useMutation ( { mutationFn : createUser , onSuccess : ( ) => { // Invalidate and refetch queryClient . invalidateQueries ( { queryKey : [ "users" ] } ) ; } , } ) ; const handleSubmit = ( data : UserData ) => { mutation . mutate ( data ) ; } ; return < Form onSubmit = { handleSubmit } isLoading = { mutation . isPending } /> ; } 3. Error Handling Comprehensive error handling : class ApiError extends Error { constructor ( message : string , public status : number , public code ? : string ) { super ( message ) ; this . name = "ApiError" ; } } const fetchWithErrorHandling = async ( url : string , options ? : RequestInit ) => { try { const response = await fetch ( url , options ) ; if ( ! response . ok ) { const error = await response . json ( ) . catch ( ( ) => ( { } ) ) ; throw new ApiError ( error . message || "Request failed" , response . status , error . code ) ; } return response . json ( ) ; } catch ( error ) { if ( error instanceof ApiError ) { throw error ; } // Network error (no internet, timeout, etc.) throw new ApiError ( "Network error" , 0 , "NETWORK_ERROR" ) ; } } ; Retry logic : const fetchWithRetry = async ( url : string , options ? : RequestInit , retries = 3 ) => { for ( let i = 0 ; i < retries ; i ++ ) { try { return await fetchWithErrorHandling ( url , options ) ; } catch ( error ) { if ( i === retries - 1 ) throw error ; // Exponential backoff await new Promise ( ( r ) => setTimeout ( r , Math . pow ( 2 , i ) * 1000 ) ) ; } } } ; 4. Authentication Token management : import * as SecureStore from "expo-secure-store" ; const TOKEN_KEY = "auth_token" ; export const auth = { getToken : ( ) => SecureStore . getItemAsync ( TOKEN_KEY ) , setToken : ( token : string ) => SecureStore . setItemAsync ( TOKEN_KEY , token ) , removeToken : ( ) => SecureStore . deleteItemAsync ( TOKEN_KEY ) , } ; // Authenticated fetch wrapper const authFetch = async ( url : string , options : RequestInit = { } ) => { const token = await auth . getToken ( ) ; return fetch ( url , { ... options , headers : { ... options . headers , Authorization : token ? Bearer ${ token } : "" , } , } ) ; } ; Token refresh : let isRefreshing = false ; let refreshPromise : Promise < string

| null = null ; const getValidToken = async ( ) : Promise < string

=> { const token = await auth . getToken ( ) ; if ( ! token || isTokenExpired ( token ) ) { if ( ! isRefreshing ) { isRefreshing = true ; refreshPromise = refreshToken ( ) . finally ( ( ) => { isRefreshing = false ; refreshPromise = null ; } ) ; } return refreshPromise ! ; } return token ; } ; 5. Offline Support Check network status : import NetInfo from "@react-native-community/netinfo" ; // Hook for network status function useNetworkStatus ( ) { const [ isOnline , setIsOnline ] = useState ( true ) ; useEffect ( ( ) => { return NetInfo . addEventListener ( ( state ) => { setIsOnline ( state . isConnected ?? true ) ; } ) ; } , [ ] ) ; return isOnline ; } Offline-first with React Query : import { onlineManager } from "@tanstack/react-query" ; import NetInfo from "@react-native-community/netinfo" ; // Sync React Query with network status onlineManager . setEventListener ( ( setOnline ) => { return NetInfo . addEventListener ( ( state ) => { setOnline ( state . isConnected ?? true ) ; } ) ; } ) ; // Queries will pause when offline and resume when online 6. Environment Variables Using environment variables for API configuration : Expo supports environment variables with the EXPO_PUBLIC_ prefix. These are inlined at build time and available in your JavaScript code. // .env EXPO_PUBLIC_API_URL = https : / / api . example . com EXPO_PUBLIC_API_VERSION = v1 // Usage in code const API_URL = process . env . EXPO_PUBLIC_API_URL ; const fetchUsers = async ( ) => { const response = await fetch ( ${ API_URL } /users ) ; return response . json ( ) ; } ; Environment-specific configuration : // .env.development EXPO_PUBLIC_API_URL = http : / / localhost : 3000 // .env.production EXPO_PUBLIC_API_URL = https : / / api . production . com Creating an API client with environment config : // api/client.ts const BASE_URL = process . env . EXPO_PUBLIC_API_URL ; if ( ! BASE_URL ) { throw new Error ( "EXPO_PUBLIC_API_URL is not defined" ) ; } export const apiClient = { get : async < T ,

( path : string ) : Promise < T

=> { const response = await fetch ( ${ BASE_URL } ${ path } ) ; if ( ! response . ok ) throw new Error ( HTTP ${ response . status } ) ; return response . json ( ) ; } , post : async < T ,

( path : string , body : unknown ) : Promise < T

=> { const response = await fetch ( ${ BASE_URL } ${ path } , { method : "POST" , headers : { "Content-Type" : "application/json" } , body : JSON . stringify ( body ) , } ) ; if ( ! response . ok ) throw new Error ( HTTP ${ response . status } ) ; return response . json ( ) ; } , } ; Important notes : Only variables prefixed with EXPO_PUBLIC_ are exposed to the client bundle Never put secrets (API keys with write access, database passwords) in EXPO_PUBLIC_ variables—they're visible in the built app Environment variables are inlined at build time , not runtime Restart the dev server after changing .env files For server-side secrets in API routes, use variables without the EXPO_PUBLIC_ prefix TypeScript support : // types/env.d.ts declare global { namespace NodeJS { interface ProcessEnv { EXPO_PUBLIC_API_URL : string ; EXPO_PUBLIC_API_VERSION ? : string ; } } } export { } ; 7. Request Cancellation Cancel on unmount : useEffect ( ( ) => { const controller = new AbortController ( ) ; fetch ( url , { signal : controller . signal } ) . then ( ( response ) => response . json ( ) ) . then ( setData ) . catch ( ( error ) => { if ( error . name !== "AbortError" ) { setError ( error ) ; } } ) ; return ( ) => controller . abort ( ) ; } , [ url ] ) ; With React Query (automatic): // React Query automatically cancels requests when queries are invalidated // or components unmount Decision Tree User asks about networking |-- Route-level data loading (web, SDK 55+)? | -- Expo Router loaders — see references/expo-router-loaders.md | |-- Basic fetch? | -- Use fetch API with error handling | |-- Need caching/state management? | |-- Complex app -> React Query (TanStack Query) | -- Simpler needs -> SWR or custom hooks | |-- Authentication? | |-- Token storage -> expo-secure-store | -- Token refresh -> Implement refresh flow | |-- Error handling? | |-- Network errors -> Check connectivity first | |-- HTTP errors -> Parse response, throw typed errors | -- Retries -> Exponential backoff | |-- Offline support? | |-- Check status -> NetInfo | -- Queue requests -> React Query persistence | |-- Environment/API config? | |-- Client-side URLs -> EXPO_PUBLIC_ prefix in .env | |-- Server secrets -> Non-prefixed env vars (API routes only) | -- Multiple environments -> .env.development, .env.production | -- Performance? |-- Caching -> React Query with staleTime |-- Deduplication -> React Query handles this -- Cancellation -> AbortController or React Query Common Mistakes Wrong: No error handling const data = await fetch ( url ) . then ( ( r ) => r . json ( ) ) ; Right: Check response status const response = await fetch ( url ) ; if ( ! response . ok ) throw new Error ( HTTP ${ response . status } ) ; const data = await response . json ( ) ; Wrong: Storing tokens in AsyncStorage await AsyncStorage . setItem ( "token" , token ) ; // Not secure! Right: Use SecureStore for sensitive data await SecureStore . setItemAsync ( "token" , token ) ; Example Invocations User: "How do I make API calls in React Native?" -> Use fetch, wrap with error handling User: "Should I use React Query or SWR?" -> React Query for complex apps, SWR for simpler needs User: "My app needs to work offline" -> Use NetInfo for status, React Query persistence for caching User: "How do I handle authentication tokens?" -> Store in expo-secure-store, implement refresh flow User: "API calls are slow" -> Check caching strategy, use React Query staleTime User: "How do I configure different API URLs for dev and prod?" -> Use EXPO PUBLIC env vars with .env.development and .env.production files User: "Where should I put my API key?" -> Client-safe keys: EXPO PUBLIC in .env. Secret keys: non-prefixed env vars in API routes only User: "How do I load data for a page in Expo Router?" -> See references/expo-router-loaders.md for route-level loaders (web, SDK 55+). For native, use React Query or fetch.

返回排行榜