跳到主要内容
知仓学习社ZHICANG

react-patterns

React 19 performance patterns and composition architecture for Vite + Cloudflare projects. 50+ rules ranked by impact — eliminating waterfalls, bund…

联网无严重或高危命中jezweb/claude-skills

它会碰到什么

扫了多少1 个文本文件,9 KB
它会碰到什么联网
命中总数2 处
命中统计严重 0 · 高 0 · 中 0 · 低 2

这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。

技能内容

React Patterns

Performance and composition patterns for React 19 + Vite + Cloudflare Workers projects. Use as a checklist when writing new components, a review guide when auditing existing code, or a refactoring playbook when something feels slow or tangled.

Rules are ranked by impact. Fix CRITICAL issues before touching MEDIUM ones.

When to Apply

  • Writing new React components or pages
  • Reviewing code for performance issues
  • Refactoring components with too many props or re-renders
  • Debugging "why is this slow?" or "why does this re-render?"
  • Building reusable component libraries
  • Code review before merging

1. Eliminating Waterfalls (CRITICAL)

Sequential async calls where they could be parallel. The #1 performance killer.

| Pattern | Problem | Fix |

|---------|---------|-----|

| Await in sequence | const a = await getA(); const b = await getB(); | const [a, b] = await Promise.all([getA(), getB()]); |

| Fetch in child | Parent renders, then child fetches, then grandchild fetches | Hoist fetches to the highest common ancestor, pass data down |

| Suspense cascade | Multiple Suspense boundaries that resolve sequentially | One Suspense boundary wrapping all async siblings |

| Await before branch | const data = await fetch(); if (condition) { use(data); } | Move await inside the branch — don't fetch what you might not use |

| Import then render | const Component = await import('./Heavy'); return <Component /> | Use React.lazy() + <Suspense> — renders fallback instantly |

How to find them: Search for await in components. Each await is a potential waterfall. If two awaits are independent, they should be parallel.

2. Bundle Size (CRITICAL)

Every KB the user downloads is a KB they wait for.

| Pattern | Problem | Fix |

|---------|---------|-----|

| Barrel imports | import { Button } from '@/components' pulls the entire barrel file | import { Button } from '@/components/ui/button' — direct import |

| No code splitting | Heavy component loaded on every page | React.lazy(() => import('./HeavyComponent')) + <Suspense> |

| Third-party at load | Analytics/tracking loaded before the app renders | Load after hydration: useEffect(() => { import('./analytics') }, []) |

| Full library import | import _ from 'lodash' (70KB) | import debounce from 'lodash/debounce' (1KB) |

| Lucide tree-shaking | import * as Icons from 'lucide-react' (all icons) | Explicit map: import { Home, Settings } from 'lucide-react' |

| Duplicate React | Library bundles its own React → "Cannot read properties of null" | resolve.dedupe: ['react', 'react-dom'] in vite.config.ts |

How to find them: npx vite-bundle-visualizer — shows what's in your bundle.

3. Composition Architecture (HIGH)

How you structure components matters more than how you optimise them.

| Pattern | Problem | Fix |

|---------|---------|-----|

| Boolean prop explosion | <Card isCompact isClickable showBorder hasIcon isLoading> | Explicit variants: <CompactCard>, <ClickableCard> |

| Compound components | Complex component with 15 props | Split into <Dialog>, <Dialog.Trigger>, <Dialog.Content> with shared context |

| renderX props | <Layout renderSidebar={...} renderHeader={...} renderFooter={...}> | Use children + named slots: <Layout><Sidebar /><Header /></Layout> |

| Lift state | Sibling components can't share state | Move state to parent or context provider |

| Provider implementation | Consumer code knows about state management internals | Provider exposes interface { state, actions, meta } — implementation hidden |

| Inline components | function Parent() { function Child() { ... } return <Child /> } | Define Child outside Parent — inline components remount on every render |

The test: If a component has more than 5 boolean props, it needs composition, not more props.

4. Re-render Prevention (MEDIUM)

Not all re-renders are bad. Only fix re-renders that cause visible jank or wasted computation.

| Pattern | Problem | Fix |

|---------|---------|-----|

| Default object/array props | function Foo({ items = [] }) → new array ref every render | Hoist: const DEFAULT = []; function Foo({ items = DEFAULT }) |

| Derived state in effect | useEffect(() => setFiltered(items.filter(...)), [items]) | Derive during render: const filtered = useMemo(() => items.filter(...), [items]) |

| Object dependency | useEffect(() => {...}, [config]) fires every render if config is {} | Use primitive deps: useEffect(() => {...}, [config.id, config.type]) |

| Subscribe to unused state | Component reads { user, theme, settings } but only uses user | Split context or use selector: useSyncExternalStore |

| State for transient values | const [mouseX, setMouseX] = useState(0) on mousemove | Use useRef for values that change frequently but don't need re-render |

| Inline callback props | <Button onClick={() => doThing(id)} /> — new function every render | useCallback or functional setState: <Button onClick={handleClick} /> |

How to find them: React DevTools Profiler → "Why did this render?" or <React.StrictMode> double-renders in dev.

5. React 19 Specifics (MEDIUM)

Patterns that changed or are new in React 19.

| Pattern | Old (React 18) | New (React 19) |

|---------|---------------|----------------|

| Form state | useFormState | useActionState — renamed |

| Ref forwarding | forwardRef((props, ref) => ...) | function Component({ ref, ...props }) — ref is a regular prop |

| Context | useContext(MyContext) | use(MyContext) — works in conditionals and loops |

| Pending UI | Manual loading state | useTransition + startTransition for non-urgent updates |

| Route-level lazy | Works with createBrowserRouter only | Still true — <Route lazy={...}> is silently ignored with <BrowserRouter> |

| Optimistic updates | Manual state management | useOptimistic hook |

| Metadata | Helmet or manual <head> management | <title>, <meta>, <link> in component JSX — hoisted to <head> automatically |

6. Rendering Performance (MEDIUM)

| Pattern | Problem | Fix |

|---------|---------|-----|

| Layout shift on load | Content jumps when async data arrives | Skeleton screens matching final layout dimensions |

| Animate SVG directly | Janky SVG animation | Wrap in <div>, animate the div instead |

| Large list rendering | 1000+ items in a table/list | @tanstack/react-virtual for virtualised rendering |

| content-visibility | Long scrollable content renders everything upfront | content-visibility: auto on off-screen sections |

| Conditional render with && | {count && <Items />} renders 0 when count is 0 | Use ternary: {count > 0 ? <Items /> : null} |

7. Data Fetching (MEDIUM)

| Pattern | Problem | Fix |

|---------|---------|-----|

| No deduplication | Same data fetched by 3 components | TanStack Query or SWR — automatic dedup + caching |

| Fetch on mount | useEffect(() => { fetch(...) }, []) — waterfalls, no caching, no dedup | TanStack Query: useQuery({ queryKey: ['users'], queryFn: fetchUsers }) |

| No optimistic update | User clicks save, waits 2 seconds, then sees change | useMutation with onMutate for instant visual feedback |

| Stale closure in interval | setInterval captures stale state | useRef for the interval ID and current values |

| Polling without cleanup | setInterval in useEffect without clearInterval | Return cleanup: useEffect(() => { const id = setInterval(...); return () => clearInterval(id); }) |

8. Vite + Cloudflare Specifics (MEDIUM)

| Pattern | Problem | Fix |

|---------|---------|-----|

| import.meta.env in Node scripts | Undefined — only works in Vite-processed files | Use loadEnv() from vite |

| React duplicate instance | Library bundles its own React | resolve.dedupe + optimizeDeps.include in vite.config.ts |

| Radix Select empty string | <SelectItem value=""> throws | Use sentinel: <SelectItem value="__any__"> |

| React Hook Form null | {...field} passes null to Input | Spread manually: value={field.value ?? ''} |

| Env vars at edge | process.env doesn't exist in Workers | Use c.env (Hono context) or import.meta.env (Vite build-time) |

Using as a Review Checklist

When reviewing code, go through categories 1-3 (CRITICAL + HIGH) for every PR. Categories 4-8 only when performance is a concern.

/react-patterns [file or component path]

Read the file, check against rules in priority order, report findings as:

file:line — [rule] description of issue

想直接用这个技能?

本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。

它属于哪个仓库

星标★ 1,010
本站分层T1
该仓技能数63
原文件路径plugins/frontend/skills/react-patterns/SKILL.md

同一个仓库里的其他技能

看这个仓库的全部 63 个技能