react-tanstack-senior
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact + TanStack Senior Developer Skill
React + TanStack 资深开发者技能
Core Philosophy
核心理念
KISS > Clever Code
Readability > Brevity
Explicit > Implicit
Composition > Inheritance
Colocation > Separation
Type Safety > AnyKISS > 炫技代码
可读性 > 简洁性
显式 > 隐式
组合 > 继承
就近放置 > 分离
类型安全 > AnyQuick Decision Tree
快速决策树
State Management:
- Server state → TanStack Query (WAJIB)
- URL state → TanStack Router search params
- Form state → TanStack Form atau React Hook Form
- Global UI state → Zustand (bukan Redux)
- Local UI state → useState/useReducer
Routing:
- SPA → TanStack Router
- Full-stack SSR → TanStack Start
- Existing Next.js → tetap Next.js
状态管理:
- 服务端状态 → TanStack Query(必须)
- URL状态 → TanStack Router 查询参数
- 表单状态 → TanStack Form 或 React Hook Form
- 全局UI状态 → Zustand(而非Redux)
- 本地UI状态 → useState/useReducer
路由:
- SPA → TanStack Router
- 全栈SSR → TanStack Start
- 现有Next.js项目 → 继续使用Next.js
Project Setup Workflow
项目搭建流程
-
Determine project type:
- SPA/Client-only? → Vite + TanStack Router + Query
- Full-stack SSR? → TanStack Start (Vinxi-based)
- Existing project? → Incremental adoption
-
Initialize project → See folder-structure.md
-
Setup core dependencies → See recommended-libraries.md
-
确定项目类型:
- SPA/纯客户端? → Vite + TanStack Router + Query
- 全栈SSR? → TanStack Start(基于Vinxi)
- 现有项目? → 逐步适配
-
初始化项目 → 查看 folder-structure.md
-
配置核心依赖 → 查看 recommended-libraries.md
TanStack Ecosystem References
TanStack生态系统参考资料
| Library | When to Read |
|---|---|
| tanstack-query.md | Data fetching, caching, mutations |
| tanstack-router.md | Type-safe routing, loaders, search params |
| tanstack-table.md | Complex tables, sorting, filtering, pagination |
| tanstack-form.md | Form validation, field arrays, async validation |
| tanstack-start.md | Full-stack SSR framework |
| 库 | 适用场景 |
|---|---|
| tanstack-query.md | 数据获取、缓存、变更 |
| tanstack-router.md | 类型安全路由、加载器、查询参数 |
| tanstack-table.md | 复杂表格、排序、筛选、分页 |
| tanstack-form.md | 表单验证、字段数组、异步验证 |
| tanstack-start.md | 全栈SSR框架 |
Code Quality Standards
代码质量标准
Naming Conventions
命名规范
typescript
// Components: PascalCase dengan suffix deskriptif
UserProfileCard.tsx // ✓
UserCard.tsx // ✗ terlalu generic
user-profile.tsx // ✗ wrong case
// Hooks: camelCase dengan prefix 'use'
useUserProfile() // ✓
useGetUserProfile() // ✗ redundant 'Get'
getUserProfile() // ✗ missing 'use'
// Query keys: array dengan hierarchy
['users', 'list', { status }] // ✓
['usersList'] // ✗ tidak granular
`users-${status}` // ✗ string interpolation
// Files: kebab-case untuk non-components
api-client.ts // ✓
apiClient.ts // ✗ typescript
// 组件:PascalCase + 描述性后缀
UserProfileCard.tsx // ✓
UserCard.tsx // ✗ 过于通用
user-profile.tsx // ✗ 大小写错误
// Hooks:camelCase + 'use'前缀
useUserProfile() // ✓
useGetUserProfile() // ✗ 冗余的'Get'
getUserProfile() // ✗ 缺少'use'前缀
// Query键:层级化数组
['users', 'list', { status }] // ✓
['usersList'] // ✗ 不够细化
`users-${status}` // ✗ 字符串插值
// 文件:非组件使用kebab-case
api-client.ts // ✓
apiClient.ts // ✗ Component Structure Pattern
组件结构模式
typescript
// 1. Imports (grouped: external → internal → types)
import { useSuspenseQuery } from '@tanstack/react-query'
import { userQueries } from '@/features/users/api'
import type { User } from '@/features/users/types'
// 2. Types (colocated, tidak di file terpisah kecuali shared)
interface UserCardProps {
userId: string
onSelect?: (user: User) => void
}
// 3. Component (single responsibility)
export function UserCard({ userId, onSelect }: UserCardProps) {
// 3a. Queries/mutations first
const { data: user } = useSuspenseQuery(userQueries.detail(userId))
// 3b. Derived state (useMemo hanya jika expensive)
const fullName = `${user.firstName} ${user.lastName}`
// 3c. Handlers (useCallback hanya jika passed to memoized children)
const handleClick = () => onSelect?.(user)
// 3d. Early returns untuk edge cases
if (!user.isActive) return null
// 3e. JSX (clean, minimal nesting)
return (
<article onClick={handleClick} className="user-card">
<h3>{fullName}</h3>
<p>{user.email}</p>
</article>
)
}typescript
// 1. 导入(分组:外部 → 内部 → 类型)
import { useSuspenseQuery } from '@tanstack/react-query'
import { userQueries } from '@/features/users/api'
import type { User } from '@/features/users/types'
// 2. 类型(就近放置,除非是共享类型否则不单独放文件)
interface UserCardProps {
userId: string
onSelect?: (user: User) => void
}
// 3. 组件(单一职责)
export function UserCard({ userId, onSelect }: UserCardProps) {
// 3a. 优先放置查询/变更逻辑
const { data: user } = useSuspenseQuery(userQueries.detail(userId))
// 3b. 派生状态(仅在计算昂贵时使用useMemo)
const fullName = `${user.firstName} ${user.lastName}`
// 3c. 事件处理函数(仅在传递给已记忆的子组件时使用useCallback)
const handleClick = () => onSelect?.(user)
// 3d. 边缘情况提前返回
if (!user.isActive) return null
// 3e. JSX(简洁,尽量减少嵌套)
return (
<article onClick={handleClick} className="user-card">
<h3>{fullName}</h3>
<p>{user.email}</p>
</article>
)
}Anti-Patterns to AVOID
需要避免的反模式
typescript
// ❌ NEVER: useEffect untuk data fetching
useEffect(() => {
fetch('/api/users').then(setUsers)
}, [])
// ✅ ALWAYS: TanStack Query
const { data: users } = useQuery(userQueries.list())
// ❌ NEVER: Prop drilling lebih dari 2 level
<Parent userData={user}>
<Child userData={user}>
<GrandChild userData={user} />
// ✅ ALWAYS: Context atau query di level yang butuh
function GrandChild() {
const { data: user } = useQuery(userQueries.current())
}
// ❌ NEVER: Premature optimization
const value = useMemo(() => a + b, [a, b]) // simple math
// ✅ ALWAYS: Optimize only when measured
const value = a + b // just calculate
// ❌ NEVER: Index as key untuk dynamic lists
{items.map((item, i) => <Item key={i} />)}
// ✅ ALWAYS: Stable unique identifier
{items.map(item => <Item key={item.id} />)}typescript
// ❌ 绝对不要:使用useEffect进行数据获取
useEffect(() => {
fetch('/api/users').then(setUsers)
}, [])
// ✅ 一定要:使用TanStack Query
const { data: users } = useQuery(userQueries.list())
// ❌ 绝对不要:属性透传超过2层
<Parent userData={user}>
<Child userData={user}>
<GrandChild userData={user} />
// ✅ 一定要:在需要的层级使用Context或查询
function GrandChild() {
const { data: user } = useQuery(userQueries.current())
}
// ❌ 绝对不要:过早优化
const value = useMemo(() => a + b, [a, b]) // 简单计算
// ✅ 一定要:仅在有性能问题时再优化
const value = a + b // 直接计算即可
// ❌ 绝对不要:在动态列表中使用索引作为key
{items.map((item, i) => <Item key={i} />)}
// ✅ 一定要:使用稳定的唯一标识符
{items.map(item => <Item key={item.id} />)}Debugging Guide
调试指南
See debugging-guide.md for:
- React DevTools profiling
- TanStack Query DevTools
- Memory leak detection
- Performance bottleneck identification
- Common error patterns
查看 debugging-guide.md 了解:
- React DevTools 性能分析
- TanStack Query DevTools
- 内存泄漏检测
- 性能瓶颈识别
- 常见错误模式
Common Pitfalls & Bugs
常见陷阱与Bug
See common-pitfalls.md for:
- Stale closure bugs
- Race conditions
- Memory leaks patterns
- Hydration mismatches
- Query invalidation mistakes
查看 common-pitfalls.md 了解:
- 陈旧闭包Bug
- 竞态条件
- 内存泄漏模式
- Hydration不匹配
- 查询失效错误
Performance Checklist
性能检查清单
markdown
□ Bundle size < 200KB gzipped (initial)
□ Largest Contentful Paint < 2.5s
□ No unnecessary re-renders (React DevTools)
□ Images lazy loaded
□ Code splitting per route
□ Query deduplication working
□ No memory leaks (heap snapshot stable)markdown
□ 初始包体积 gzipped 小于200KB
□ 最大内容绘制时间小于2.5秒
□ 无不必要的重渲染(通过React DevTools检查)
□ 图片已懒加载
□ 按路由进行代码分割
□ 查询去重生效
□ 无内存泄漏(堆快照稳定)Code Review Checklist
代码评审检查清单
markdown
□ No `any` types (use `unknown` if needed)
□ No `// @ts-ignore` tanpa penjelasan
□ Error boundaries di route level
□ Loading states handled
□ Empty states handled
□ Error states handled
□ Accessibility (aria labels, keyboard nav)
□ No hardcoded strings (i18n ready)
□ No console.log in production code
□ Tests untuk business logicmarkdown
□ 无`any`类型(必要时使用`unknown`)
□ 无未加说明的`// @ts-ignore`
□ 路由层级已添加错误边界
□ 已处理加载状态
□ 已处理空状态
□ 已处理错误状态
□ 可访问性(aria标签、键盘导航)
□ 无硬编码字符串(支持国际化)
□ 生产代码中无console.log
□ 业务逻辑已编写测试