前端工程Technical Deep Dive
React 生态工具链 —— 数据获取、状态管理、表格实战
发布时间2026/03/29
分类前端工程
预计阅读3 分钟
作者吴长龙
*
React 生态全景
01.内容
React 生态全景
经过多年发展,React 生态已非常成熟。本文介绍数据获取、状态管理、表格等领域的主流工具。
一、数据获取:React Query / SWR
#### 为什么需要数据获取库?
jsx snippetjsx
// 原生 fetch 的问题
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(data => {
setUsers(data);
setLoading(false);
})
.catch(err => {
setError(err);
setLoading(false);
});
}, []);
if (loading) return <Loading />;
if (error) return <Error error={error} />;
return <List users={users} />;
}问题:手写太多样板代码,需要处理 loading、error、缓存、轮询、预加载...
#### React Query(TanStack Query)
jsx snippetjsx
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<UserList />
</QueryClientProvider>
);
}
function UserList() {
const { data, isLoading, error, refetch } = useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(res => res.json()),
staleTime: 5 * 60 * 1000, // 5分钟内数据不过期
retry: 3, // 失败重试3次
});
if (isLoading) return <Loading />;
if (error) return <Error error={error} />;
return (
<div>
<button onClick={() => refetch()}>刷新</button>
<List users={data} />
</div>
);
}核心特性:
- •自动缓存:相同 queryKey 自动复用缓存
- •后台更新:staleTime 后自动重新获取
- •去重请求:相同请求合并为一个
- •乐观更新:更新后立即反映,稍后同步服务器
jsx snippetjsx
// 乐观更新示例
const mutation = useMutation({
mutationFn: updateUser,
onMutate: async (newUser) => {
// 取消当前进行的请求
await queryClient.cancelQueries(['users', newUser.id]);
// 快照旧数据
const oldData = queryClient.getQueryData(['users', newUser.id]);
// 立即更新缓存
queryClient.setQueryData(['users', newUser.id], newUser);
// 返回上下文,供失败时回滚
return { oldData };
},
onError: (err, newUser, context) => {
// 回滚到旧数据
queryClient.setQueryData(['users', newUser.id], context.oldData);
},
onSettled: (data, err, variables) => {
// 重新获取确保一致
queryClient.invalidateQueries(['users']);
},
});#### SWR
SWR 是 Vercel 出的轻量级方案,API 类似但更简洁:
jsx snippetjsx
import useSWR from 'swr';
function UserList() {
const { data, error, isLoading, mutate } = useSWR('/api/users', fetcher);
if (isLoading) return <Loading />;
if (error) return <Error error={error} />;
return (
<div>
<button onClick={() => mutate()}>刷新</button>
<List users={data} />
</div>
);
}SWR vs React Query 对比:
| 特性 | SWR | React Query |
|---|---|---|
| 体积 | ~6KB | ~12KB |
| API | 简洁 | 功能丰富 |
| DevTools | 基础 | 完善 |
| 适用 | 简单场景 | 复杂场景 |
二、表格:TanStack Table
#### 为什么需要表格库?
表格是企业级应用最复杂的组件之一,需要处理:
- •排序、筛选、分页
- •列固定、行展开
- •虚拟滚动(大数据)
- •行列合并
- •编辑模式
TanStack Table(原 React Table)是 headless 方案,帮你处理逻辑,样式完全自定义。
jsx snippetjsx
import { useReactTable, getCoreRowModel, getSortedRowModel } from '@tanstack/react-table';
function DataTable({ data }) {
const [sorting, setSorting] = useState([]);
const table = useReactTable({
data,
columns: [
{ header: 'Name', accessorKey: 'name' },
{ header: 'Age', accessorKey: 'age' },
{
header: 'Actions',
cell: ({ row }) => (
<button onClick={() => handleEdit(row.original)}>编辑</button>
)
}
],
state: { sorting },
onSortingChange: setSorting,
getCoreRowModel: getCoreRowModel(),
getSortedRowModel: getSortedRowModel(),
});
return (
<table>
<thead>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => (
<th
key={header.id}
onClick={header.column.getToggleSortingHandler()}
>
{header.column.getIsSorted() === 'asc' ? '↑' :
header.column.getIsSorted() === 'desc' ? '↓' : ''}
{flexRender(header.column.columnDef.header, header.getContext())}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(row => (
<tr key={row.id}>
{row.getVisibleCells().map(cell => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
);
}核心特性:
- •Headless:完全控制 UI
- •功能丰富:排序、筛选、分页、展开...
- •类型安全:TypeScript 支持完善
- •性能好:支持虚拟滚动
三、表单:React Hook Form
jsx snippetjsx
import { useForm, Controller } from 'react-hook-form';
function LoginForm() {
const { control, handleSubmit, formState: { errors } } = useForm({
defaultValues: { email: '', password: '' }
});
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="email"
control={control}
rules={{ required: '邮箱必填', pattern: /^\S+@\S+$/ }}
render={({ field }) => (
<div>
<input {...field} />
{errors.email && <span>{errors.email.message}</span>}
</div>
)}
/>
<Controller
name="password"
control={control}
rules={{ required: '密码必填', minLength: 6 }}
render={({ field }) => (
<div>
<input type="password" {...field} />
{errors.password && <span>{errors.password.message}</span>}
</div>
)}
/>
<button type="submit">登录</button>
</form>
);
}优点:
- •受控/非受控模式灵活切换
- •性能好:只重渲染变化的字段
- •验证规则丰富
- •体积小(~9KB)
四、可视化:Recharts / Visx
#### Recharts
jsx snippetjsx
import { LineChart, Line, XAxis, YAxis, Tooltip } from 'recharts';
function SalesChart({ data }) {
return (
<LineChart data={data}>
<XAxis dataKey="month" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="sales" stroke="#8884d8" />
</LineChart>
);
}#### Visx
D3-based 的 React 可视化库,更底层但更灵活:
jsx snippetjsx
import { Group } from '@visx/group';
import { LinePath } from '@visx/shape';
function CustomChart({ data }) {
return (
<svg width={500} height={300}>
<Group>
<LinePath
data={data}
x={d => xScale(d.date)}
y={d => yScale(d.value)}
stroke="#8884d8"
strokeWidth={2}
/>
</Group>
</svg>
);
}五、拖拽:dnd-kit
jsx snippetjsx
import { DndContext, closestCenter } from '@dnd-kit/core';
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
function SortableList({ items, setItems }) {
function handleDragEnd(event) {
const { active, over } = event;
if (active.id !== over.id) {
const oldIndex = items.findIndex(i => i.id === active.id);
const newIndex = items.findIndex(i => i.id === over.id);
// 重新排序...
}
}
return (
<DndContext collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
<SortableContext items={items} strategy={verticalListSortingStrategy}>
{items.map(item => <SortableItem key={item.id} item={item} />)}
</SortableContext>
</DndContext>
);
}选型建议
| 场景 | 推荐方案 |
|---|---|
| 数据获取 | React Query(复杂)或 SWR(简单) |
| 表格 | TanStack Table |
| 表单 | React Hook Form |
| 可视化 | Recharts(简单)或 Visx(复杂) |
| 拖拽 | dnd-kit |
| 状态管理 | 参见上篇文章 |
总结
React 生态的核心思想是组合:
- •每个库专注一件事
- •通过组合构建复杂应用
- •选择适合项目规模的工具
不要过度工程化:小项目用 fetch + useState,中等项目用 SWR + Zustand,大型项目用 React Query + 自定义状态管理。