npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill react-api深度知识:使用
mcp__documentation__fetch_docs并指定技术:react以获取全面的文档。
在以下情况下请跳过此技能:
state-tanstack-query)state-tanstack-query)import { useState, useEffect, useCallback } from 'react';
interface UseFetchState<T> {
data: T | null;
loading: boolean;
error: Error | null;
}
interface UseFetchOptions {
immediate?: boolean;
}
export function useFetch<T>(
url: string,
options?: RequestInit & UseFetchOptions
) {
const [state, setState] = useState<UseFetchState<T>>({
data: null,
loading: options?.immediate !== false,
error: null,
});
const execute = useCallback(async () => {
setState(prev => ({ ...prev, loading: true, error: null }));
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
setState({ data, loading: false, error: null });
return data;
} catch (error) {
setState(prev => ({
...prev,
loading: false,
error: error as Error,
}));
throw error;
}
}, [url, options]);
useEffect(() => {
if (options?.immediate !== false) {
execute();
}
}, [execute, options?.immediate]);
return { ...state, refetch: execute };
}
// Usage
function UserProfile({ id }: { id: string }) {
const { data: user, loading, error, refetch } = useFetch<User>(
`/api/users/${id}`
);
if (loading) return <Spinner />;
if (error) return <Error message={error.message} onRetry={refetch} />;
return <div>{user?.name}</div>;
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
npm install swr
import useSWR from 'swr';
const fetcher = (url: string) => fetch(url).then(res => res.json());
function UserProfile({ id }: { id: string }) {
const { data, error, isLoading, mutate } = useSWR<User>(
`/api/users/${id}`,
fetcher
);
if (isLoading) return <Spinner />;
if (error) return <Error />;
return <div>{data?.name}</div>;
}
import { SWRConfig } from 'swr';
const fetcher = async (url: string) => {
const res = await fetch(url, {
headers: { Authorization: `Bearer ${getToken()}` },
});
if (!res.ok) throw new Error('API Error');
return res.json();
};
function App() {
return (
<SWRConfig
value={{
fetcher,
revalidateOnFocus: true,
revalidateOnReconnect: true,
dedupingInterval: 2000,
errorRetryCount: 3,
onError: (error) => console.error(error),
}}
>
<MyApp />
</SWRConfig>
);
}
import useSWRMutation from 'swr/mutation';
async function createUser(url: string, { arg }: { arg: CreateUserDto }) {
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(arg),
});
return res.json();
}
function CreateUserForm() {
const { trigger, isMutating } = useSWRMutation('/api/users', createUser);
const handleSubmit = async (data: CreateUserDto) => {
try {
await trigger(data);
// Success
} catch (error) {
// Error
}
};
return <form onSubmit={handleSubmit}>...</form>;
}
function UserList() {
const { data, mutate } = useSWR<User[]>('/api/users', fetcher);
const deleteUser = async (id: string) => {
// Optimistic update
const optimisticData = data?.filter(u => u.id !== id);
mutate(optimisticData, { revalidate: false });
try {
await fetch(`/api/users/${id}`, { method: 'DELETE' });
mutate(); // Revalidate after success
} catch {
mutate(data); // Rollback on error
}
};
return (
<ul>
{data?.map(user => (
<li key={user.id}>
{user.name}
<button onClick={() => deleteUser(user.id)}>Delete</button>
</li>
))}
</ul>
);
}
// React 18+ with use() hook
import { use, Suspense } from 'react';
// Create promise outside component
const userPromise = fetch('/api/users/1').then(res => res.json());
function UserProfile() {
const user = use(userPromise);
return <div>{user.name}</div>;
}
function App() {
return (
<Suspense fallback={<Spinner />}>
<UserProfile />
</Suspense>
);
}
import useSWR from 'swr';
function UserProfile({ id }: { id: string }) {
const { data } = useSWR<User>(`/api/users/${id}`, fetcher, {
suspense: true,
});
// data is guaranteed to exist (no loading state)
return <div>{data.name}</div>;
}
function App() {
return (
<Suspense fallback={<Spinner />}>
<UserProfile id="1" />
</Suspense>
);
}
import { Component, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
onError?: (error: Error) => void;
}
interface State {
hasError: boolean;
error: Error | null;
}
export class ApiErrorBoundary extends Component<Props, State> {
state: State = { hasError: false, error: null };
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error) {
this.props.onError?.(error);
}
reset = () => {
this.setState({ hasError: false, error: null });
};
render() {
if (this.state.hasError) {
return this.props.fallback ?? (
<div>
<h2>Something went wrong</h2>
<button onClick={this.reset}>Try again</button>
</div>
);
}
return this.props.children;
}
}
// Usage
function App() {
return (
<ApiErrorBoundary
fallback={<ErrorFallback />}
onError={(error) => logError(error)}
>
<Suspense fallback={<Spinner />}>
<UserProfile />
</Suspense>
</ApiErrorBoundary>
);
}
function useSSE<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
setData(JSON.parse(event.data));
};
eventSource.onerror = () => {
setError(new Error('SSE connection failed'));
eventSource.close();
};
return () => eventSource.close();
}, [url]);
return { data, error };
}
// Usage
function Notifications() {
const { data: notification } = useSSE<Notification>('/api/notifications');
return notification ? <Toast message={notification.message} /> : null;
}
function useWebSocket<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [isConnected, setIsConnected] = useState(false);
const wsRef = useRef<WebSocket | null>(null);
useEffect(() => {
const ws = new WebSocket(url);
wsRef.current = ws;
ws.onopen = () => setIsConnected(true);
ws.onclose = () => setIsConnected(false);
ws.onmessage = (event) => setData(JSON.parse(event.data));
return () => ws.close();
}, [url]);
const send = useCallback((message: unknown) => {
wsRef.current?.send(JSON.stringify(message));
}, []);
return { data, isConnected, send };
}
// Usage
function Chat() {
const { data: message, send, isConnected } = useWebSocket<Message>(
'wss://api.example.com/chat'
);
const handleSend = (text: string) => {
send({ type: 'message', text });
};
return (
<div>
{isConnected ? 'Connected' : 'Connecting...'}
{message && <Message data={message} />}
</div>
);
}
function UserCardSkeleton() {
return (
<div className="animate-pulse">
<div className="h-12 w-12 bg-gray-200 rounded-full" />
<div className="h-4 w-32 bg-gray-200 mt-2 rounded" />
<div className="h-3 w-24 bg-gray-200 mt-1 rounded" />
</div>
);
}
function UserCard({ id }: { id: string }) {
const { data, isLoading } = useSWR<User>(`/api/users/${id}`, fetcher);
if (isLoading) return <UserCardSkeleton />;
return (
<div>
<img src={data?.avatar} />
<h3>{data?.name}</h3>
<p>{data?.email}</p>
</div>
);
}
| 反模式 | 为何不好 | 正确方法 |
|---|---|---|
| 在 useEffect 中获取数据但未清理 | 竞态条件 | 使用 AbortController |
| 未处理加载/错误状态 | 用户体验差 | 始终显示加载/错误界面 |
| 在渲染中获取数据 | 无限循环 | 使用 useEffect 或 Suspense |
| 无请求去重 | 重复请求 | 使用 SWR 或 TanStack Query |
| 硬编码 API URL | 难以维护 | 使用环境变量 |
| 未重试失败的请求 | 容错性差 | 添加重试逻辑 |
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 无限获取循环 | 在渲染中获取数据 | 移至 useEffect |
| 显示过时数据 | 无重新验证 | 配置 SWR 重新验证 |
| 竞态条件 | 多个快速请求 | 使用 AbortController |
| 内存泄漏警告 | 更新已卸载的组件 | 检查挂载状态或清理 |
| 重复请求 | 无去重 | 使用 SWR 或缓存层 |
| CORS 错误 | 后端未配置 | 配置 CORS 头 |
深度知识:使用
mcp__documentation__fetch_docs并指定技术:react以获取全面的文档。
每周安装次数
1
代码仓库
首次出现
3 天前
安全审计
安装于
amp1
cline1
openclaw1
opencode1
cursor1
kimi-cli1
Deep Knowledge : Use
mcp__documentation__fetch_docswith technology:reactfor comprehensive documentation.
Skip this skill when:
state-tanstack-query)state-tanstack-query)import { useState, useEffect, useCallback } from 'react';
interface UseFetchState<T> {
data: T | null;
loading: boolean;
error: Error | null;
}
interface UseFetchOptions {
immediate?: boolean;
}
export function useFetch<T>(
url: string,
options?: RequestInit & UseFetchOptions
) {
const [state, setState] = useState<UseFetchState<T>>({
data: null,
loading: options?.immediate !== false,
error: null,
});
const execute = useCallback(async () => {
setState(prev => ({ ...prev, loading: true, error: null }));
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
setState({ data, loading: false, error: null });
return data;
} catch (error) {
setState(prev => ({
...prev,
loading: false,
error: error as Error,
}));
throw error;
}
}, [url, options]);
useEffect(() => {
if (options?.immediate !== false) {
execute();
}
}, [execute, options?.immediate]);
return { ...state, refetch: execute };
}
// Usage
function UserProfile({ id }: { id: string }) {
const { data: user, loading, error, refetch } = useFetch<User>(
`/api/users/${id}`
);
if (loading) return <Spinner />;
if (error) return <Error message={error.message} onRetry={refetch} />;
return <div>{user?.name}</div>;
}
npm install swr
import useSWR from 'swr';
const fetcher = (url: string) => fetch(url).then(res => res.json());
function UserProfile({ id }: { id: string }) {
const { data, error, isLoading, mutate } = useSWR<User>(
`/api/users/${id}`,
fetcher
);
if (isLoading) return <Spinner />;
if (error) return <Error />;
return <div>{data?.name}</div>;
}
import { SWRConfig } from 'swr';
const fetcher = async (url: string) => {
const res = await fetch(url, {
headers: { Authorization: `Bearer ${getToken()}` },
});
if (!res.ok) throw new Error('API Error');
return res.json();
};
function App() {
return (
<SWRConfig
value={{
fetcher,
revalidateOnFocus: true,
revalidateOnReconnect: true,
dedupingInterval: 2000,
errorRetryCount: 3,
onError: (error) => console.error(error),
}}
>
<MyApp />
</SWRConfig>
);
}
import useSWRMutation from 'swr/mutation';
async function createUser(url: string, { arg }: { arg: CreateUserDto }) {
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(arg),
});
return res.json();
}
function CreateUserForm() {
const { trigger, isMutating } = useSWRMutation('/api/users', createUser);
const handleSubmit = async (data: CreateUserDto) => {
try {
await trigger(data);
// Success
} catch (error) {
// Error
}
};
return <form onSubmit={handleSubmit}>...</form>;
}
function UserList() {
const { data, mutate } = useSWR<User[]>('/api/users', fetcher);
const deleteUser = async (id: string) => {
// Optimistic update
const optimisticData = data?.filter(u => u.id !== id);
mutate(optimisticData, { revalidate: false });
try {
await fetch(`/api/users/${id}`, { method: 'DELETE' });
mutate(); // Revalidate after success
} catch {
mutate(data); // Rollback on error
}
};
return (
<ul>
{data?.map(user => (
<li key={user.id}>
{user.name}
<button onClick={() => deleteUser(user.id)}>Delete</button>
</li>
))}
</ul>
);
}
// React 18+ with use() hook
import { use, Suspense } from 'react';
// Create promise outside component
const userPromise = fetch('/api/users/1').then(res => res.json());
function UserProfile() {
const user = use(userPromise);
return <div>{user.name}</div>;
}
function App() {
return (
<Suspense fallback={<Spinner />}>
<UserProfile />
</Suspense>
);
}
import useSWR from 'swr';
function UserProfile({ id }: { id: string }) {
const { data } = useSWR<User>(`/api/users/${id}`, fetcher, {
suspense: true,
});
// data is guaranteed to exist (no loading state)
return <div>{data.name}</div>;
}
function App() {
return (
<Suspense fallback={<Spinner />}>
<UserProfile id="1" />
</Suspense>
);
}
import { Component, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: ReactNode;
onError?: (error: Error) => void;
}
interface State {
hasError: boolean;
error: Error | null;
}
export class ApiErrorBoundary extends Component<Props, State> {
state: State = { hasError: false, error: null };
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
componentDidCatch(error: Error) {
this.props.onError?.(error);
}
reset = () => {
this.setState({ hasError: false, error: null });
};
render() {
if (this.state.hasError) {
return this.props.fallback ?? (
<div>
<h2>Something went wrong</h2>
<button onClick={this.reset}>Try again</button>
</div>
);
}
return this.props.children;
}
}
// Usage
function App() {
return (
<ApiErrorBoundary
fallback={<ErrorFallback />}
onError={(error) => logError(error)}
>
<Suspense fallback={<Spinner />}>
<UserProfile />
</Suspense>
</ApiErrorBoundary>
);
}
function useSSE<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
setData(JSON.parse(event.data));
};
eventSource.onerror = () => {
setError(new Error('SSE connection failed'));
eventSource.close();
};
return () => eventSource.close();
}, [url]);
return { data, error };
}
// Usage
function Notifications() {
const { data: notification } = useSSE<Notification>('/api/notifications');
return notification ? <Toast message={notification.message} /> : null;
}
function useWebSocket<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [isConnected, setIsConnected] = useState(false);
const wsRef = useRef<WebSocket | null>(null);
useEffect(() => {
const ws = new WebSocket(url);
wsRef.current = ws;
ws.onopen = () => setIsConnected(true);
ws.onclose = () => setIsConnected(false);
ws.onmessage = (event) => setData(JSON.parse(event.data));
return () => ws.close();
}, [url]);
const send = useCallback((message: unknown) => {
wsRef.current?.send(JSON.stringify(message));
}, []);
return { data, isConnected, send };
}
// Usage
function Chat() {
const { data: message, send, isConnected } = useWebSocket<Message>(
'wss://api.example.com/chat'
);
const handleSend = (text: string) => {
send({ type: 'message', text });
};
return (
<div>
{isConnected ? 'Connected' : 'Connecting...'}
{message && <Message data={message} />}
</div>
);
}
function UserCardSkeleton() {
return (
<div className="animate-pulse">
<div className="h-12 w-12 bg-gray-200 rounded-full" />
<div className="h-4 w-32 bg-gray-200 mt-2 rounded" />
<div className="h-3 w-24 bg-gray-200 mt-1 rounded" />
</div>
);
}
function UserCard({ id }: { id: string }) {
const { data, isLoading } = useSWR<User>(`/api/users/${id}`, fetcher);
if (isLoading) return <UserCardSkeleton />;
return (
<div>
<img src={data?.avatar} />
<h3>{data?.name}</h3>
<p>{data?.email}</p>
</div>
);
}
| Anti-Pattern | Why It's Bad | Correct Approach |
|---|---|---|
| Fetching in useEffect without cleanup | Race conditions | Use AbortController |
| Not handling loading/error states | Poor UX | Always show loading/error UI |
| Fetching in render | Infinite loops | Use useEffect or Suspense |
| No request deduplication | Duplicate requests | Use SWR or TanStack Query |
| Hardcoded API URLs | Hard to maintain | Use environment variables |
| Not retrying failed requests | Poor resilience | Add retry logic |
| Issue | Likely Cause | Solution |
|---|---|---|
| Infinite fetch loop | Fetching in render | Move to useEffect |
| Stale data shown | No revalidation | Configure SWR revalidation |
| Race condition | Multiple rapid requests | Use AbortController |
| Memory leak warning | Updating unmounted component | Check mounted state or cleanup |
| Duplicate requests | No deduplication | Use SWR or caching layer |
| CORS error | Backend not configured | Configure CORS headers |
Deep Knowledge : Use
mcp__documentation__fetch_docswith technology:reactfor comprehensive documentation.
Weekly Installs
1
Repository
First Seen
3 days ago
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
amp1
cline1
openclaw1
opencode1
cursor1
kimi-cli1
Lark CLI IM 即时消息管理工具:机器人/用户身份操作聊天、消息、文件下载
20,000 周安装
Docnify自动化:通过Rube MCP和Composio工具包实现文档操作自动化
1 周安装
Docmosis自动化集成指南:通过Rube MCP与Composio实现文档生成自动化
1 周安装
Dictionary API自动化教程:通过Rube MCP和Composio实现词典API操作自动化
1 周安装
detrack-automation:自动化追踪技能,集成Claude AI提升开发效率
1 周安装
Demio自动化工具包:通过Rube MCP和Composio实现Demio操作自动化
1 周安装
Deel自动化工具:通过Rube MCP与Composio实现HR与薪资操作自动化
1 周安装