react-best-practices
This React Best Practices skill provides patterns for hooks, effects, refs, and component design. It emphasizes using effects only for synchronizing with external systems and avoiding common anti-patterns like derived state in effects, expensive calculations without useMemo, and improper state resets. Use this skill when reading or writing React components in TypeScript or JavaScript files.
git clone --depth 1 https://github.com/aiskillstore/marketplace /tmp/react-best-practices && cp -r /tmp/react-best-practices/skills/0xbigboss/react-best-practices ~/.claude/skills/react-best-practicesSKILL.md
# React Best Practices
## Pair with TypeScript
When working with React, always load both this skill and `typescript-best-practices` together. TypeScript patterns (type-first development, discriminated unions, Zod validation) apply to React code.
## Core Principle: Effects Are Escape Hatches
Effects let you "step outside" React to synchronize with external systems. **Most component logic should NOT use Effects.** Before writing an Effect, ask: "Is there a way to do this without an Effect?"
## When to Use Effects
Effects are for synchronizing with **external systems**:
- Subscribing to browser APIs (WebSocket, IntersectionObserver, resize)
- Connecting to third-party libraries not written in React
- Setting up/cleaning up event listeners on window/document
- Fetching data on mount (though prefer React Query or framework data fetching)
- Controlling non-React DOM elements (video players, maps, modals)
## When NOT to Use Effects
### Derived State (Calculate During Render)
```tsx
// BAD: Effect for derived state
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
const [fullName, setFullName] = useState('');
useEffect(() => {
setFullName(firstName + ' ' + lastName);
}, [firstName, lastName]);
// GOOD: Calculate during render
const [firstName, setFirstName] = useState('Taylor');
const [lastName, setLastName] = useState('Swift');
const fullName = firstName + ' ' + lastName;
```
### Expensive Calculations (Use useMemo)
```tsx
// BAD: Effect for caching
const [visibleTodos, setVisibleTodos] = useState([]);
useEffect(() => {
setVisibleTodos(getFilteredTodos(todos, filter));
}, [todos, filter]);
// GOOD: useMemo for expensive calculations
const visibleTodos = useMemo(
() => getFilteredTodos(todos, filter),
[todos, filter]
);
```
### Resetting State on Prop Change (Use key)
```tsx
// BAD: Effect to reset state
function ProfilePage({ userId }) {
const [comment, setComment] = useState('');
useEffect(() => {
setComment('');
}, [userId]);
// ...
}
// GOOD: Use key to reset component state
function ProfilePage({ userId }) {
return <Profile userId={userId} key={userId} />;
}
function Profile({ userId }) {
const [comment, setComment] = useState(''); // Resets automatically
// ...
}
```
### User Event Handling (Use Event Handlers)
```tsx
// BAD: Event-specific logic in Effect
function ProductPage({ product, addToCart }) {
useEffect(() => {
if (product.isInCart) {
showNotification(`Added ${product.name} to cart`);
}
}, [product]);
// ...
}
// GOOD: Logic in event handler
function ProductPage({ product, addToCart }) {
function buyProduct() {
addToCart(product);
showNotification(`Added ${product.name} to cart`);
}
// ...
}
```
### Notifying Parent of State Changes
```tsx
// BAD: Effect to notify parent
function Toggle({ onChange }) {
const [isOn, setIsOn] = useState(false);
useEffect(() => {
onChange(isOn);
}, [isOn, onChange]);
// ...
}
// GOOD: Update both in event handler
function Toggle({ onChange }) {
const [isOn, setIsOn] = useState(false);
function updateToggle(nextIsOn) {
setIsOn(nextIsOn);
onChange(nextIsOn);
}
// ...
}
// BEST: Fully controlled component
function Toggle({ isOn, onChange }) {
function handleClick() {
onChange(!isOn);
}
// ...
}
```
### Chains of Effects
```tsx
// BAD: Effect chain
useEffect(() => {
if (card !== null && card.gold) {
setGoldCardCount(c => c + 1);
}
}, [card]);
useEffect(() => {
if (goldCardCount > 3) {
setRound(r => r + 1);
setGoldCardCount(0);
}
}, [goldCardCount]);
// GOOD: Calculate derived state, update in event handler
const isGameOver = round > 5;
function handlePlaceCard(nextCard) {
setCard(nextCard);
if (nextCard.gold) {
if (goldCardCount < 3) {
setGoldCardCount(goldCardCount + 1);
} else {
setGoldCardCount(0);
setRound(round + 1);
}
}
}
```
## Effect Dependencies
### Never Suppress the Linter
```tsx
// BAD: Suppressing linter hides bugs
useEffect(() => {
const id = setInterval(() => {
setCount(count + increment);
}, 1000);
return () => clearInterval(id);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// GOOD: Fix the code, not the linter
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + increment);
}, 1000);
return () => clearInterval(id);
}, [increment]);
```
### Use Updater Functions to Remove State Dependencies
```tsx
// BAD: messages in dependencies causes reconnection on every message
useEffect(() => {
connection.on('message', (msg) => {
setMessages([...messages, msg]);
});
// ...
}, [messages]); // Reconnects on every message!
// GOOD: Updater function removes dependency
useEffect(() => {
connection.on('message', (msg) => {
setMessages(msgs => [...msgs, msg]);
});
// ...
}, []); // No messages dependency needed
```
### Move Objects/Functions Inside Effects
```tsx
// BAD: Object created each render triggers Effect
function ChatRoom({ roomId }) {
const options = { serverUrl, roomId }; // New object each render
useEffect(() => {
const connection = createConnection(options);
connection.connect();
return () => connection.disconnect();
}, [options]); // Reconnects every render!
}
// GOOD: Create object inside Effect
function ChatRoom({ roomId }) {
useEffect(() => {
const options = { serverUrl, roomId };
const connection = createConnection(options);
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]); // Only reconnects when values change
}
```
### useEffectEvent for Non-Reactive Logic
```tsx
// BAD: theme change reconnects chat
function ChatRoom({ roomId, theme }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
showNotification('Connected!', theme);
});
connection.connect();
return ()Implement SAFe methodology in Jira. Use when creating Epics, Features, Stories with proper hierarchy, acceptance criteria, and parent-child linking.
Orchestrate Jira workflows end-to-end. Use when building stories with approvals, transitioning items through lifecycle states, or syncing task completion with Jira.
HSK4級レベルから流暢さを目指す学習者向け。中国語表現の使用場面・自然さを分析し、作文を「ネイティブらしい流暢な表現」に改善。bilibili等のコンテンツ理解とネイティブとの会話をサポート。実際の用例をWeb検索で提示
Next.js 15 애플리케이션을 위한 프론트엔드 개발 가이드라인. React 19, TypeScript, Shadcn/ui, Tailwind CSS를 사용한 모던 패턴. Server Components, Client Components, App Router, 파일 구조, Shadcn/ui 컴포넌트, 성능 최적화, TypeScript 모범 사례 포함. 컴포넌트, 페이지, 기능 생성, 데이터 페칭, 스타일링, 라우팅, 프론트엔드 코드 작업 시 사용.
Claude Code 스킬, 훅, 에이전트, 명령어를 생성하고 관리하기 위한 메타 스킬. 새 스킬 생성, 스킬 트리거 설정, 훅 설정, Claude Code 인프라 관리 시 사용.
Discover and extract sitemaps from any website using SitemapKit. Use this skill whenever the user wants to find pages on a website, get a list of URLs from a domain, audit a site's structure, crawl a sitemap, check what pages exist on a site, or do anything involving sitemaps or site URL discovery — even if they don't explicitly say "sitemap". Requires the sitemapkit MCP server configured with a valid SITEMAPKIT_API_KEY.
GitHubのプルリクエスト(PR)を作成する際に使用します。変更のコミット、プッシュ、PR作成を含む完全なワークフローを日本語で実行します。「PRを作って」「プルリクエストを作成」「pull requestを作成」などのリクエストで自動的に起動します。
Generate an SVG of a user-requested image or scene