
关于
适用于实现或调试任何网络请求、API 调用或数据获取。涵盖 Fetch API、React Query、SWR、错误处理、缓存、离线支持和 Expo Router 数据加载器(useLoaderData)。
name: native-data-fetching description: 在实现或调试任何网络请求、API 调用或数据获取时使用。涵盖 fetch API、React Query、SWR、错误处理、缓存、离线支持和 Expo Router 数据加载器(useLoaderData)。 risk: unknown source: community version: 1.0.0 license: MIT
Expo 网络
你必须在任何网络相关工作中使用此技能,包括 API 请求、数据获取、缓存或网络调试。
参考资料
根据需要查阅以下资源:
references/
expo-router-loaders.md 使用 Expo Router 加载器的路由级数据加载(web,SDK 55+)
何时使用
在以下情况使用此技能:
- 实现 API 请求
- 设置数据获取(React Query、SWR)
- 使用 Expo Router 数据加载器(
useLoaderData,web SDK 55+) - 调试网络故障
- 实现缓存策略
- 处理离线场景
- 认证/令牌管理
- 配置 API URL 和环境变量
偏好
- 避免使用 axios,优先使用 expo/fetch
常见问题与解决方案
1. 基本 Fetch 用法
简单 GET 请求:
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 请求:
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)
设置:
// 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>
);
}
获取数据:
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} />;
}
变更操作:
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. 错误处理
全面的错误处理:
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");
}
};
重试逻辑:
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. 认证
令牌管理:
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,
Authori
兼容工具
Claude CodeCursor
标签
后端开发
