跳到主内容
智客 ZICQ

技能库 智客分类:Agent 工作流 expo-data-fetching

Expo Data Fetching

执行或调试任何网络请求、 API 调用或获取数据时使用 。 封面获取 API, React Query, SWR, 错误处理, 缓存, 离线支持, 装入/空入/ 误入屏幕状态, 以及 Expo Router 数据加载器( 使用 LoaderData) .

33055 安装量

官方网址:skills.sh

技能介绍

先看中文介绍;官方 description 原文单独保留,不改写 SKILL.md。

做什么

执行或调试任何网络请求、 API 调用或获取数据时使用 。 封面获取 API, React Query, SWR, 错误处理, 缓存, 离线支持, 装入/空入/ 误入屏幕状态, 以及 Expo Router 数据加载器( 使用 LoaderData) .

何时用

当 :

代理如何加载

按 Agent Skills 渐进披露:启动时只加载 name 与 description(约 100 token);任务匹配后才读入整份 SKILL.md 正文;scripts/、references/、assets/ 仅在需要时再读。 本文件正文结构:Expo Networking、References、When to Use、Preferences、Every Screen Has Four States、Common Issues & Solutions。

文件分析

文件分析:除 SKILL.md 外,正文引用了 references/offline-and-cancellation.md、references/expo-router-loaders.md,属于带资源的技能包,这些文件按需再读。

官方 description(原文)

Use when implementing or debugging ANY network request, API call, or data fetching. Covers fetch API, React Query, SWR, error handling, caching, offline support, loading/empty/error screen states, and Expo Router data loaders (`useLoaderData`).

Expo NetworkingReferencesWhen to UsePreferencesEvery Screen Has Four StatesCommon Issues & Solutions1. Basic Fetch Usage2. React Query (TanStack Query)3. Error Handling4. Authentication5. Offline Support6. Environment Variables

· 许可:MIT

来源分类:skills.sh agent-skill

SKILL.md 与 Agent 调用

官方规范 ↗
name
expo-data-fetching
description
Use when implementing or debugging ANY network request, API call, or data fetching. Covers fetch API, React Query, SWR, error handling, caching, offline support, loading/empty/error screen states, and Expo Router data loaders (`useLoaderData`).
许可
MIT
  1. 发现技能客户端向 Agent 提供名称与描述目录。
  2. 匹配与调用用户指定或任务匹配后,载入 SKILL.md 指令。
  3. 按需加载按步骤读取参考文档、使用脚本与素材。
指令中引用的文件 · 2
  • references/offline-and-cancellation.md
  • references/expo-router-loaders.md

以下路径提取自原文;文件是否齐全请以来源仓库中的完整目录为准。

具体调用语法与可用工具以目标 Agent 客户端为准。 查看调用机制说明 ↗

安装这个技能

Skills CLI ↗

先选择目标 Agent 和安装范围,保留技能包的附属文件,安装后检查客户端能否发现该技能。

该技能引用了附属文件,请从来源获取完整目录;仅复制 SKILL.md 可能缺少依赖。

交给 Agent 安装

复制安装指令给支持 Agent Skills 的代理,确认其中的目标目录与客户端匹配。

把 Agent Skill「expo-data-fetching」安装到我的项目:SKILL.md 原文与官方 description 见 https://zicq.com/zh/skills/skl-5e2b98fe533c248f-Expo-Data-Fetching.html
请存为 .cursor/skills/expo-data-fetching/SKILL.md 或 .claude/skills/expo-data-fetching/SKILL.md,frontmatter 的 name 与 description 保持原样,不要改写。
该技能还带 scripts/、references/、assets/ 等文件,请从 https://github.com/expo/skills 取完整目录,不要只建一个 SKILL.md。

GitHub 完整包 ↗

终端安装 · Skills CLI

需要 Node.js 与 npx。先查看仓库技能列表,确认实际名称。

npx skills add 'https://github.com/expo/skills' --list

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

CLI 会交互选择目标 Agent,默认安装到项目;用户级安装使用 -g。先通过查看命令核对仓库内容,再用 npx skills list 检查已安装技能。

阅读排版
--- name: expo-data-fetching description: Use when implementing or debugging ANY network request, API call, or data fetching. Covers fetch API, React Query, SWR, error handling, caching, offline support, loading/empty/error screen states, and Expo Router data loaders (`useLoaderData`). license: MIT --- # 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+) offline-and-cancellation.md NetInfo network status, offline-first React Query, AbortController ``` ## 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 ## Every Screen Has Four States Design **loading**, **error**, **empty**, and **content** for screens that load data. These can overlap: a refresh error should coexist with cached content. - **Loading ≠ empty.** Empty means *resolved with zero items*, not missing data. Handle initial loading, failure, and hydration before checking list length. In TanStack Query v5, `isLoading` means the first fetch is running; a disabled or offline-paused query can have no data without being loading. Show the prerequisite or offline state in that case. - **Empty is a designed state, not a blank list.** Use `ListEmptyComponent` on FlatList/FlashList: explain why it is empty and offer the relevant next action. "No items yet" can offer Create; "No results" should offer changing or clearing the search/filter. - **Refetches keep stale content.** Render cached `data` even if a refresh fails, with a nonblocking error and retry. Use `isLoading` for first-fetch spinners and `isFetching` for background activity; prefer a skeleton for a slow initial load with a known layout, and `RefreshControl` for user-initiated refresh. - **Gate on hydration.** When initial UI or a redirect depends on persisted state (auth token, onboarding flag), the root layout renders nothing - or the splash - until that state has loaded. Deciding on unhydrated state flashes the wrong screen on every cold start and misroutes deep links that arrive before hydration. **Saves preserve work.** While a mutation is pending, disable repeat submission. On failure, retain the draft, show an inline error, and let the user retry; clear or dismiss only after success. If updating optimistically, restore the previous value or mark the edit as unsynced on failure. Verify with a failed save followed by retry. ## Common Issues & Solutions ### 1. Basic Fetch Usage **Simple GET request**: ```tsx 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**: ```tsx 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**: ```tsx // 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 ( ); } ``` **Fetching data**: ```tsx import { useQuery } from "@tanstack/react-query"; function UserProfile({ userId }: { userId: string }) { const { data, fetchStatus, error, refetch } = useQuery({ queryKey: ["user", userId], queryFn: () => fetchUser(userId), }); if (data === undefined) { if (error) return refetch()} />; if (fetchStatus === "paused") return ; return ; } return ( <> {error && refetch()} />} {data === null ? : } ); } ``` **Mutations**: ```tsx 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) => { if (mutation.isPending) return; mutation.mutate(data); }; // Form keeps its draft on error and disables Submit while isLoading. return ; } ``` --- ### 3. Error Handling **Comprehensive error handling**: ```tsx 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**: ```tsx 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**: ```tsx 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**: ```tsx let isRefreshing = false; let refreshPromise: Promise | null = null; const getValidToken = async (): Promise => { 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 Network-status detection with NetInfo and offline-first React Query setup: see [./references/offline-and-cancellation.md](./references/offline-and-cancellation.md). --- ### 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. ```tsx // .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**: ```tsx // .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**: ```tsx // 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 (path: string): Promise => { const response = await fetch(`${BASE_URL}${path}`); if (!response.ok) throw new Error(`HTTP ${response.status}`); return response.json(); }, post: async (path: string, body: unknown): Promise => { 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**: ```tsx // types/env.d.ts declare global { namespace NodeJS { interface ProcessEnv { EXPO_PUBLIC_API_URL: string; EXPO_PUBLIC_API_VERSION?: string; } } } export {}; ``` --- ### 7. Request Cancellation AbortController on unmount (React Query cancels automatically): see [./references/offline-and-cancellation.md](./references/offline-and-cancellation.md). --- ## 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** ```tsx const data = await fetch(url).then((r) => r.json()); ``` **Right: Check response status** ```tsx const response = await fetch(url); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); ``` **Wrong: Storing tokens in AsyncStorage** ```tsx await AsyncStorage.setItem("token", token); // Not secure! ``` **Right: Use SecureStore for sensitive data** ```tsx 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. ## Submitting Feedback You can voluntarily report errors, misleading or outdated information in this skill, so Expo can improve it: ```bash npx --yes @expo/agent-cli@latest feedback --category skills --subject "expo-data-fetching" --message "" ``` Use `--category agent-cli` for a problem with the agent CLI, and `--category evals` when an agent repeatedly fails at an Expo task. Run `feedback --help` for subjects and the data that the command sends. Each report goes to Expo with project and environment details. Show the user the message, and send it only if the user agrees. If you cannot ask the user, do not send it. Do not include secrets or personal data.

相关技能

Agent 工作流

Skill Creator

创造有效技能指南。 当用户想创造出新的技能(或更新现有的技能),以专业知识,工作流程,或工具集成来扩展克洛德的能力时,应该使用这种技能.

Agent 工作流

Clawdhub

使用ClawdHub CLI搜索,安装,更新并发布从taladhub.com的代理技能. 需要获取苍蝇上的新技能时使用,将安装的技能同步到最新版本或特定版本,或者发布 npm-instainddhub CLI 的新/更新的技能文件夹.

Agent 工作流

Agent Team Orchestration

管弦乐团多代理团队,任务设定周期,交接协议,审查工作流程. 使用时间: (1)建立2+特派员队伍,具有不同专业,(2)确定任务路线和生命周期(收录框_ spec_建设_审查_完成),(3)在特派员之间制定交接协议,(4)建立审查和质量关口,(5)管理特派员之间的交流和文物共享.

Agent 工作流

Superpowers

Spec-first,TDD,子代理驱动的软件开发工作流程. 当:(1)构建任何新功能或应用——触发脑暴_计划_子代理执行回路,(2)调试出一个bug或测试失败——触发系统性的根起过程,(3)用户说"让我们构建","帮助我计划","我想添加X",或"这个被打破",(4)完成一个功…