Skip to main content

react-best-practices

Use when sequential user actions hit multiple endpoints needing the same data within seconds.

跳到安装

来源信息

仓库
tomevault-io/claude-code-plugins
最近来源活动
2026年4月6日 09:16
检测到的 SKILL.md 语言
英语
星标
3
分支
2

安装方式

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

检查来源文件

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

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
react-best-practices
description
Use when sequential user actions hit multiple endpoints needing the same data within seconds.
metadata
{"author":"davila7"}
# React Best Practices **Version 0.1.0** Vercel Engineering January 2026 > **Note:** > This document is mainly for agents and LLMs to follow when maintaining, > generating, or refactoring React and Next.js codebases at Vercel. Humans > may also find it useful, but guidance here is optimized for automation > and consistency by AI-assisted workflows. --- ## Abstract Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation. --- ## Table of Contents 1. [Eliminating Waterfalls](#1-eliminating-waterfalls) — **CRITICAL** - 1.1 [Defer Await Until Needed](#11-defer-await-until-needed) - 1.2 [Dependency-Based Parallelization](#12-dependency-based-parallelization) - 1.3 [Prevent Waterfall Chains in API Routes](#13-prevent-waterfall-chains-in-api-routes) - 1.4 [Promise.all() for Independent Operations](#14-promiseall-for-independent-operations) - 1.5 [Strategic Suspense Boundaries](#15-strategic-suspense-boundaries) 2. [Bundle Size Optimization](#2-bundle-size-optimization) — **CRITICAL** - 2.1 [Avoid Barrel File Imports](#21-avoid-barrel-file-imports) - 2.2 [Conditional Module Loading](#22-conditional-module-loading) - 2.3 [Defer Non-Critical Third-Party Libraries](#23-defer-non-critical-third-party-libraries) - 2.4 [Dynamic Imports for Heavy Components](#24-dynamic-imports-for-heavy-components) - 2.5 [Preload Based on User Intent](#25-preload-based-on-user-intent) 3. [Server-Side Performance](#3-server-side-performance) — **HIGH** - 3.1 [Cross-Request LRU Caching](#31-cross-request-lru-caching) - 3.2 [Minimize Serialization at RSC Boundaries](#32-minimize-serialization-at-rsc-boundaries) - 3.3 [Parallel Data Fetching with Component Composition](#33-parallel-data-fetching-with-component-composition) - 3.4 [Per-Request Deduplication with React.cache()](#34-per-request-deduplication-with-reactcache) - 3.5 [Use after() for Non-Blocking Operations](#35-use-after-for-non-blocking-operations) 4. [Client-Side Data Fetching](#4-client-side-data-fetching) — **MEDIUM-HIGH** - 4.1 [Deduplicate Global Event Listeners](#41-deduplicate-global-event-listeners) - 4.2 [Use SWR for Automatic Deduplication](#42-use-swr-for-automatic-deduplication) 5. [Re-render Optimization](#5-re-render-optimization) — **MEDIUM** - 5.1 [Defer State Reads to Usage Point](#51-defer-state-reads-to-usage-point) - 5.2 [Extract to Memoized Components](#52-extract-to-memoized-components) - 5.3 [Narrow Effect Dependencies](#53-narrow-effect-dependencies) - 5.4 [Subscribe to Derived State](#54-subscribe-to-derived-state) - 5.5 [Use Functional setState Updates](#55-use-functional-setstate-updates) - 5.6 [Use Lazy State Initialization](#56-use-lazy-state-initialization) - 5.7 [Use Transitions for Non-Urgent Updates](#57-use-transitions-for-non-urgent-updates) 6. [Rendering Performance](#6-rendering-performance) — **MEDIUM** - 6.1 [Animate SVG Wrapper Instead of SVG Element](#61-animate-svg-wrapper-instead-of-svg-element) - 6.2 [CSS content-visibility for Long Lists](#62-css-content-visibility-for-long-lists) - 6.3 [Hoist Static JSX Elements](#63-hoist-static-jsx-elements) - 6.4 [Optimize SVG Precision](#64-optimize-svg-precision) - 6.5 [Prevent Hydration Mismatch Without Flickering](#65-prevent-hydration-mismatch-without-flickering) - 6.6 [Use Activity Component for Show/Hide](#66-use-activity-component-for-showhide) - 6.7 [Use Explicit Conditional Rendering](#67-use-explicit-conditional-rendering) 7. [JavaScript Performance](#7-javascript-performance) — **LOW-MEDIUM** - 7.1 [Batch DOM CSS Changes](#71-batch-dom-css-changes) - 7.2 [Build Index Maps for Repeated Lookups](#72-build-index-maps-for-repeated-lookups) - 7.3 [Cache Property Access in Loops](#73-cache-property-access-in-loops) - 7.4 [Cache Repeated Function Calls](#74-cache-repeated-function-calls) - 7.5 [Cache Storage API Calls](#75-cache-storage-api-calls) - 7.6 [Combine Multiple Array Iterations](#76-combine-multiple-array-iterations) - 7.7 [Early Length Check for Array Comparisons](#77-early-length-check-for-array-comparisons) - 7.8 [Early Return from Functions](#78-early-return-from-functions) - 7.9 [Hoist RegExp Creation](#79-hoist-regexp-creation) - 7.10 [Use Loop for Min/Max Instead of Sort](#710-use-loop-for-minmax-instead-of-sort) - 7.11 [Use Set/Map for O(1) Lookups](#711-use-setmap-for-o1-lookups) - 7.12 [Use toSorted() Instead of sort() for Immutability](#712-use-tosorted-instead-of-sort-for-immutability) 8. [Advanced Patterns](#8-advanced-patterns) — **LOW** - 8.1 [Store Event Handlers in Refs](#81-store-event-handlers-in-refs) - 8.2 [useLatest for Stable Callback Refs](#82-uselatest-for-stable-callback-refs) --- ## 1. Eliminating Waterfalls **Impact: CRITICAL** Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains. ### 1.1 Defer Await Until Needed **Impact: HIGH (avoids blocking unused code paths)** Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them. **Incorrect: blocks both branches** ```typescript async function handleRequest(userId: string, skipProcessing: boolean) { const userData = await fetchUserData(userId) if (skipProcessing) { // Returns immediately but still waited for userData return { skipped: true } } // Only this branch uses userData return processUserData(userData) } ``` **Correct: only blocks when needed** ```typescript async function handleRequest(userId: string, skipProcessing: boolean) { if (skipProcessing) { // Returns immediately without waiting return { skipped: true } } // Fetch only when needed const userData = await fetchUserData(userId) return processUserData(userData) } ``` **Another example: early return optimization** ```typescript // Incorrect: always fetches permissions async function updateResource(resourceId: string, userId: string) { const permissions = await fetchPermissions(userId) const resource = await getResource(resourceId) if (!resource) { return { error: 'Not found' } } if (!permissions.canEdit) { return { error: 'Forbidden' } } return await updateResourceData(resource, permissions) } // Correct: fetches only when needed async function updateResource(resourceId: string, userId: string) { const resource = await getResource(resourceId) if (!resource) { return { error: 'Not found' } } const permissions = await fetchPermissions(userId) if (!permissions.canEdit) { return { error: 'Forbidden' } } return await updateResourceData(resource, permissions) } ``` This optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive. ### 1.2 Dependency-Based Parallelization **Impact: CRITICAL (2-10× improvement)** For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment. **Incorrect: profile waits for config unnecessarily** ```typescript const [user, config] = await Promise.all([ fetchUser(), fetchConfig() ]) const profile = await fetchProfile(user.id) ``` **Correct: config and profile run in parallel** ```typescript import { all } from 'better-all' const { user, config, profile } = await all({ async user() { return fetchUser() }, async config() { return fetchConfig() }, async profile() { return fetchProfile((await this.$.user).id) } }) ``` Reference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all) ### 1.3 Prevent Waterfall Chains in API Routes **Impact: CRITICAL (2-10× improvement)** In API routes and Server Actions, start independent operations immediately, even if you don't await them yet. **Incorrect: config waits for auth, data waits for both** ```typescript export async function GET(request: Request) { const session = await auth() const config = await fetchConfig() const data = await fetchData(session.user.id) return Response.json({ data, config }) } ``` **Correct: auth and config start immediately** ```typescript export async function GET(request: Request) { const sessionPromise = auth() const configPromise = fetchConfig() const session = await sessionPromise const [config, data] = await Promise.all([ configPromise, fetchData(session.user.id) ]) return Response.json({ data, config }) } ``` For operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization). ### 1.4 Promise.all() for Independent Operations **Impact: CRITICAL (2-10× improvement)** When async operations have no interdependencies, execute them concurrently using `Promise.all()`. **Incorrect: sequential execution, 3 round trips** ```typescript const user = await fetchUser() const posts = await fetchPosts() const comments = await fetchComments() ``` **Correct: parallel execution, 1 round trip** ```typescript const [user, posts, comments] = await Promise.all([ fetchUser(), fetchPosts(), fetchComments() ]) ``` ### 1.5 Strategic Suspense Boundaries **Impact: HIGH (faster initial paint)** Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads. **Incorrect: wrapper blocked by data fetching** ```tsx async function Page() { const data = await fetchData() // Blocks entire page return ( <div> <div>Sidebar</div> <div>Header</div> <div> <DataDisplay data={data} /> </div> <div>Footer</div> </div> ) } ``` The entire layout waits for data even though only the middle section needs it. **Correct: wrapper shows immediately, data streams in** ```tsx function Page() { return ( <div> <div>Sidebar</div> <div>Header</div> <div> <Suspense fallback={<Skeleton />}> <DataDisplay /> </Suspense> </div> <div>Footer</div> </div> ) } async function DataDisplay() { const data = await fetchData() // Only blocks this component return <div>{data.content}</div> } ``` Sidebar, Header, and Footer render immediately. Only DataDisplay waits for data. **Alternative: share promise across components** ```tsx function Page() { // Start fetch immediately, but don't await const dataPromise = fetchData() return ( <div> <div>Sidebar</div> <div>Header</div> <Suspense fallback={<Skeleton />}> <DataDisplay dataPromise={dataPromise} /> <DataSummary dataPromise={dataPromise} /> </Suspense> <div>Footer</div> </div> ) } function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) { const data = use(dataPromise) // Unwraps the promise return <div>{data.content}</div> } function DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) { const data = use(dataPromise) // Reuses the same promise return <div>{data.summary}</div> } ``` Both components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together. **When NOT to use this pattern:** - Critical data needed for layout decisions (affects positioning) - SEO-critical content above the fold - Small, fast queries where suspense overhead isn't worth it - When you want to avoid layout shift (loading → content jump) **Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities. --- ## 2. Bundle Size Optimization **Impact: CRITICAL** Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint. ### 2.1 Avoid Barrel File Imports **Impact: CRITICAL (200-800ms import cost, slow builds)** Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`). Popular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts. **Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph. **Incorrect: imports entire library** ```tsx import { Check, X, Menu } from 'lucide-react' // Loads 1,583 modules, takes ~2.8s extra in dev // Runtime cost: 200-800ms on every cold start import { Button, TextField } from '@mui/material' // Loads 2,225 modules, takes ~4.2s extra in dev ``` **Correct: imports only what you need** ```tsx import Check from 'lucide-react/dist/esm/icons/check'
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看