react-performance-patterns
Battle-tested patterns for optimizing React applications, from component design to bundle optimization
来源信息
- 仓库
- anaghkanungo7/agent-skills
- 最近来源活动
- 2026年2月12日 15:26
- 检测到的 SKILL.md 语言
- 英语
- 星标
- 1
- 分支
- 0
安装方式
默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。
检查来源文件
决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。
文件资源管理器
3 个文件正在显示 SKILL.md
SKILL.md
来源说明 · 只读预览- name
- react-performance-patterns
- description
- Battle-tested patterns for optimizing React applications, from component design to bundle optimization
# React Performance Patterns
You are an expert in React performance optimization. You help developers identify performance bottlenecks, implement efficient rendering patterns, and build fast, responsive React applications. Your guidance is based on real-world production experience and current best practices.
## Core Performance Principles
### 1. Measure Before Optimizing
Never optimize blindly. Always profile first:
```tsx
// Use React DevTools Profiler
import { Profiler } from 'react';
function onRenderCallback(
id: string,
phase: 'mount' | 'update',
actualDuration: number,
baseDuration: number,
startTime: number,
commitTime: number
) {
console.log({ id, phase, actualDuration });
}
<Profiler id="ExpensiveComponent" onRender={onRenderCallback}>
<ExpensiveComponent />
</Profiler>
```
**Key metrics to track:**
- Render count
- Render duration
- Component mount/update time
- Bundle size
- Network waterfall
- Core Web Vitals (LCP, INP, CLS)
### 2. Avoid Unnecessary Renders
React re-renders when:
- State changes
- Props change
- Parent re-renders
- Context value changes
Your job: minimize wasted renders.
### 3. Code Split Aggressively
Users shouldn't download code for pages they never visit.
### 4. Optimize Heavy Operations
Move expensive calculations off the main thread or cache results.
## Pattern 1: Memoization
### React.memo() - Prevent Component Re-renders
```tsx
// Before: Re-renders on every parent render
function UserCard({ user }: { user: User }) {
return <div>{user.name}</div>;
}
// After: Only re-renders when user prop changes
const UserCard = memo(({ user }: { user: User }) => {
return <div>{user.name}</div>;
});
```
**When to use:**
- Pure functional components
- Components that render frequently with same props
- Expensive render operations
- List items
**When NOT to use:**
- Component always receives new props
- Props contain objects/arrays created inline
- Component is already fast
### useMemo() - Memoize Expensive Calculations
```tsx
function DataTable({ data, filters }: Props) {
// Bad: Recalculates on every render
const filtered = data.filter(item =>
filters.every(f => f.fn(item))
);
// Good: Only recalculates when data or filters change
const filtered = useMemo(
() => data.filter(item => filters.every(f => f.fn(item))),
[data, filters]
);
return <Table data={filtered} />;
}
```
**When to use:**
- Filtering/sorting large arrays
- Complex calculations
- Derived data that's expensive to compute
- Creating objects/arrays passed as props
**Cost/benefit check:**
```tsx
// Not worth it (simple operation)
const doubled = useMemo(() => count * 2, [count]);
// Worth it (expensive operation)
const sorted = useMemo(
() => items.sort((a, b) => expensiveCompare(a, b)),
[items]
);
```
### useCallback() - Memoize Functions
```tsx
function Parent() {
// Bad: New function on every render
const handleClick = () => {
doSomething();
};
// Good: Stable function reference
const handleClick = useCallback(() => {
doSomething();
}, []);
return <MemoizedChild onClick={handleClick} />;
}
```
**When to use:**
- Passing callbacks to memoized children
- Dependencies in useEffect/useMemo
- Creating stable event handlers
- Working with debounce/throttle
**Common mistake:**
```tsx
// Mistake: useCallback with new object in dependency
const handleClick = useCallback(() => {
doSomething(data);
}, [data]); // If 'data' is a new object each render, callback still changes
// Better: Destructure stable values
const { id, name } = data;
const handleClick = useCallback(() => {
doSomething({ id, name });
}, [id, name]);
```
## Pattern 2: Code Splitting & Lazy Loading
### Component-Level Code Splitting
```tsx
import { lazy, Suspense } from 'react';
// Bad: Bundles everything upfront
import HeavyChart from './HeavyChart';
import AdminPanel from './AdminPanel';
// Good: Load on demand
const HeavyChart = lazy(() => import('./HeavyChart'));
const AdminPanel = lazy(() => import('./AdminPanel'));
function Dashboard() {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(true)}>
Show Chart
</button>
{showChart && (
<Suspense fallback={<Skeleton />}>
<HeavyChart />
</Suspense>
)}
</div>
);
}
```
### Route-Based Code Splitting
```tsx
// Next.js: Automatic code splitting by route
// app/dashboard/page.tsx
export default function DashboardPage() {
return <Dashboard />;
}
// React Router: Manual code splitting
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
function App() {
return (
<Routes>
<Route
path="/dashboard"
element={
<Suspense fallback={<Spinner />}>
<Dashboard />
</Suspense>
}
/>
<Route
path="/profile"
element={
<Suspense fallback={<Spinner />}>
<Profile />
</Suspense>
}
/>
</Routes>
);
}
```
### Preloading for Better UX
```tsx
// Preload on hover (faster perceived performance)
function Navigation() {
const handleMouseEnter = () => {
// Preload Dashboard chunk
import('./pages/Dashboard');
};
return (
<Link
to="/dashboard"
onMouseEnter={handleMouseEnter}
>
Dashboard
</Link>
);
}
```
## Pattern 3: Virtualization (Long Lists)
For lists with 100+ items, render only what's visible.
### Using react-window
```tsx
import { FixedSizeList } from 'react-window';
// Bad: Rendering 10,000 items
function BadList({ items }: { items: Item[] }) {
return (
<div>
{items.map(item => (
<ItemRow key={item.id} item={item} />
))}
</div>
);
}
// Good: Virtualized (only renders ~20 visible items)
function GoodList({ items }: { items: Item[] }) {
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{({ index, style }) => (
<div style={style}>
<ItemRow item={items[index]} />
</div>
)}
</FixedSizeList>
);
}
```
**Performance impact:**
- 10,000 items without virtualization: 5-10 second render
- 10,000 items with virtualization: 50-100ms render
### Dynamic Size Lists
```tsx
import { VariableSizeList } from 'react-window';
function DynamicList({ items }: { items: Item[] }) {
const getItemSize = (index: number) => {
// Return height based on content
return items[index].type === 'large' ? 120 : 60;
};
return (
<VariableSizeList
height={600}
itemCount={items.length}
itemSize={getItemSize}
width="100%"
>
{({ index, style }) => (
<div style={style}>
<ItemRow item={items[index]} />
</div>
)}
</VariableSizeList>
);
}
```
## Pattern 4: Optimize Context Usage
### Problem: Context Causes Unnecessary Re-renders
```tsx
// Bad: Every consumer re-renders on any state change
const AppContext = createContext<AppState>(null);
function AppProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const [notifications, setNotifications] = useState<Notification[]>([]);
const value = { user, setUser, theme, setTheme, notifications, setNotifications };
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
}
// This re-renders when ANYTHING in context changes
function UserAvatar() {
const { user } = useContext(AppContext); // Re-renders on theme change!
return <Avatar user={user} />;
}
```
### Solution 1: Split Contexts
```tsx
// Good: Separate concerns
const UserContext = createContext<UserState>(null);
const ThemeContext = createContext<ThemeState>(null);
const NotificationContext = createContext<NotificationState>(null);
// Now components only subscribe to what they need
function UserAvatar() {
const { user } = useContext(UserContext); // Only re-renders on user change
return <Avatar user={user} />;
}
```
### Solution 2: Context Selectors
```tsx
// Using zustand (or similar library)
import create from 'zustand';
const useStore = create<AppState>((set) => ({
user: null,
theme: 'light',
setUser: (user) => set({ user }),
setTheme: (theme) => set({ theme }),
}));
// Select only what you need
function UserAvatar() {
const user = useStore((state) => state.user); // Only re-renders on user change
return <Avatar user={user} />;
}
function ThemeSwitcher() {
const theme = useStore((state) => state.theme); // Only re-renders on theme change
return <ThemeToggle theme={theme} />;
}
```
### Solution 3: Memoize Context Value
```tsx
function AppProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
// Prevent new object on every render
const value = useMemo(
() => ({ user, setUser }),
[user]
);
return <AppContext.Provider value={value}>{children}</AppContext.Provider>;
}
```
## Pattern 5: Debounce & Throttle
### Debounce: Wait for User to Stop Typing
```tsx
import { useDebouncedCallback } from 'use-debounce';
function SearchInput() {
const [query, setQuery] = useState('');
// Bad: API call on every keystroke
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setQuery(value);
searchAPI(value); // 🔥 Too many requests!
};
// Good: Wait 300ms after user stops typing
const debouncedSearch = useDebouncedCallback(
(value: string) => {
searchAPI(value);
},
300
);
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setQuery(value);
debouncedSearch(value);
};
return <input value={query} onChange={handleChange} />;
}
```
**Custom debounce hook:**
```tsx
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
// Usage
function SearchResults() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);
useEffect(() => {
在 GitHub 查看这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看