Skip to main content

data

Use when implementing data fetching, API calls, server/client components, or SWR hooks

来源信息

仓库
trycompai/comp
最近来源活动
2026年4月27日 13:56
检测到的 SKILL.md 语言
英语
星标
2,035
分支
433

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
data
description
Use when implementing data fetching, API calls, server/client components, or SWR hooks
Source Cursor rule: `.cursor/rules/data.mdc`. Original Cursor alwaysApply: `false`. # Data Fetching ## Core Pattern: Server → Client → SWR ### 1. Server Page Fetches Data ```tsx // app/(app)/[orgId]/tasks/page.tsx export default async function TasksPage({ params }: { params: Promise<{ orgId: string }> }) { const { orgId } = await params; // From URL, NOT session const tasks = await getTasks(orgId); return <TaskListClient organizationId={orgId} initialTasks={tasks} />; } ``` ### 2. Client Component Receives Initial Data ```tsx // components/TaskListClient.tsx 'use client'; export function TaskListClient({ organizationId, initialTasks }: Props) { const { tasks, createTask, updateTask } = useTasks({ organizationId, initialData: initialTasks, }); // Initial render is instant - no loading state } ``` ### 3. SWR Hook with fallbackData ```tsx // hooks/useTasks.ts export function useTasks({ organizationId, initialData }: UseTasksOptions) { const { data, mutate } = useSWR( ['/v1/tasks', organizationId], // Include orgId for cache isolation async ([endpoint, orgId]) => { const response = await apiClient.get(endpoint, orgId); return response.data?.tasks ?? []; }, { fallbackData: initialData } ); const createTask = async (input: CreateTaskInput) => { await apiClient.post('/v1/tasks', input, organizationId); mutate(); // Revalidate }; const updateTask = async ({ taskId, input }: { taskId: string; input: UpdateTaskInput }) => { await apiClient.put(`/v1/tasks/${taskId}`, input, organizationId); mutate(); // Revalidate }; return { tasks: data ?? [], createTask, updateTask, mutate }; } ``` ## API Client Use `apiClient` from `@/lib/api-client`: ```tsx import { apiClient } from '@/lib/api-client'; await apiClient.get<ResponseType>('/v1/endpoint', organizationId); await apiClient.post<ResponseType>('/v1/endpoint', body, organizationId); await apiClient.put<ResponseType>('/v1/endpoint', body, organizationId); await apiClient.delete('/v1/endpoint', organizationId); ``` ## Server vs Client Components **Layouts = server.** Interactive logic in separate client components. ```tsx // layout.tsx (server) export default function Layout({ children }) { return ( <PageLayout> <PageHeader title="Title" /> <ClientTabs /> {/* Client component */} {children} </PageLayout> ); } // components/ClientTabs.tsx 'use client'; export function ClientTabs() { const router = useRouter(); // Interactive logic here } ``` ## State Management **No `nuqs`** - use React state or Next.js patterns: ```tsx // ✅ React state for UI const [isOpen, setIsOpen] = useState(false); // ✅ Next.js for URL state const router = useRouter(); const searchParams = useSearchParams(); // ❌ No nuqs import { useQueryState } from 'nuqs'; ``` ## Rules ```tsx // ✅ Always const { orgId } = await params; // From URL params const { data } = useSWR(key, f, { fallbackData }); // With initial data await apiClient.get('/v1/endpoint', orgId); // Use apiClient useSWR(['/v1/tasks', orgId], fetcher); // Include orgId in key // ❌ Never const orgId = session?.activeOrganizationId; // From session const { data } = useSWR('/api/data'); // No initial data await fetch('/api/endpoint'); // Direct fetch ``` ## File Structure ``` app/(app)/[orgId]/tasks/ ├── page.tsx # Server - fetches data ├── components/ │ └── TaskListClient.tsx # Client - receives initialData ├── hooks/ │ └── useTasks.ts # SWR hook with mutations └── data/ └── queries.ts # Server-side queries ```
在 GitHub 查看