本文へ移動
cccskills
無料GitHub で公開

react-hooks

This skill should be used when the user asks to "review React hooks", "check useEffect usage", "optimize hooks performance", "design custom hooks", "refactor to use hooks", "remove unnecessary useEffect", "simplify useMemo usage", or discusses React hooks patterns. Provides guidance on writing minimal, effective React hooks code.

日本語の概要は準備中です。原文の説明を表示しています。

インストール方法を見る

含まれるファイル(6)

  • SKILL.md11.6 KB
  • examples/anti-patterns.tsx11.3 KB
  • examples/good-patterns.tsx9.0 KB
  • references/custom-hooks.md14.3 KB
  • references/dependency-array.md12.1 KB
  • references/unnecessary-hooks.md11.1 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

React Hooks Best Practices

This skill provides guidance for writing clean, efficient React hooks code with emphasis on eliminating unnecessary hooks and following established best practices.

Core Philosophy: Less Is More

The primary principle is to use hooks only when necessary. Many React applications suffer from hook overuse, particularly:

  • useEffect for logic that should be in event handlers
  • useMemo/useCallback for cheap computations
  • useState for derived state

Before adding any hook, ask: "Can this be done without a hook?"

Mandatory Rules

1. Linter Compliance is Non-Negotiable

Never suppress react-hooks/exhaustive-deps warnings. The ESLint plugin understands React's rules better than manual reasoning. If a warning appears:

  • Add missing dependencies
  • Use functional updates to remove dependencies
  • Restructure code to eliminate the need
  • Never use // eslint-disable-next-line

2. Constants at Module Level, Not in Hooks

Never use useMemo for constant values. Define unchanging values at module scope:

// ❌ WRONG: useMemo for constants
function Component() {
  const options = useMemo(
    () => [
      { value: "a", label: "Option A" },
      { value: "b", label: "Option B" },
    ],
    [],
  );
}

// ✅ CORRECT: Module-level constants
const OPTIONS = [
  { value: "a", label: "Option A" },
  { value: "b", label: "Option B" },
] as const;

function Component() {
  // Just use OPTIONS directly
}

3. Custom Hook Returns Must Be Stable

All values returned from custom hooks must have stable references:

// ❌ WRONG: Unstable return values
function useData(id: string) {
  const [data, setData] = useState(null);
  return {
    data,
    refresh: () => fetch(id), // New function every render
    meta: { id }, // New object every render
  };
}

// ✅ CORRECT: All returns memoized or stable
function useData(id: string) {
  const [data, setData] = useState(null);

  const refresh = useCallback(() => fetch(id), [id]);
  const meta = useMemo(() => ({ id }), [id]);

  return { data, refresh, meta };
}

4. Prefer Modern Hooks When Available

Use experimental/newer hooks when React version permits:

HookPurposeReplaces
useEffectEventStable event callbacks in effectsuseRef + manual sync
useRead resources in renderuseEffect + useState
useOptimisticOptimistic UI updatesManual state management
useFormStatusForm submission stateCustom loading state

5. Prefer Suspense for Async Operations

Prefer React Suspense boundaries over manual loading state management:

// ❌ AVOID: Manual loading state in component
function UserProfile({ userId }: Props) {
  const { data, isLoading, error } = useUser(userId);

  if (isLoading) return <Spinner />;
  if (error) return <ErrorDisplay error={error} />;
  return <Profile user={data} />;
}

// ✅ PREFER: Suspense boundary with data hook
function UserProfile({ userId }: Props) {
  const user = useUser(userId); // Suspends until ready
  return <Profile user={user} />;
}

// Parent handles loading/error
<ErrorBoundary fallback={<ErrorDisplay />}>
  <Suspense fallback={<Spinner />}>
    <UserProfile userId={id} />
  </Suspense>
</ErrorBoundary>;

Note: Internal hook implementations may still use { data, error, loading } structures. The preference for Suspense applies to component-level API design.

Quick Decision Framework

When NOT to Use useEffect

ScenarioWrong ApproachCorrect Approach
Transform data for renderinguseEffect + useStateCalculate during render
Respond to user eventuseEffect watching stateEvent handler directly
Initialize from propsuseEffect syncing propsCompute in render or use key
Fetch on mount onlyuseEffect with []Use data fetching library

When NOT to Use useMemo/useCallback

ScenarioSkip Memoization When...
Simple calculationsOperation is O(1) or simple array methods
Non-object returnsReturning primitives (string, number, boolean)
No child optimizationChild components don't use React.memo
Development phasePremature optimization without profiling

Essential Patterns

1. Derived State: Calculate, Don't Store

// ❌ Anti-pattern: Storing derived state
const [items, setItems] = useState<Item[]>([]);
const [filteredItems, setFilteredItems] = useState<Item[]>([]);

useEffect(() => {
  setFilteredItems(items.filter((item) => item.active));
}, [items]);

// ✅ Correct: Calculate during render
const [items, setItems] = useState<Item[]>([]);
const filteredItems = items.filter((item) => item.active);

2. Event Responses: Use Handlers, Not Effects

// ❌ Anti-pattern: Effect watching state
const [query, setQuery] = useState("");

useEffect(() => {
  if (query) {
    analytics.track("search", { query });
  }
}, [query]);

// ✅ Correct: Track in event handler
const handleSearch = (newQuery: string) => {
  setQuery(newQuery);
  if (newQuery) {
    analytics.track("search", { query: newQuery });
  }
};

3. Parent-Child Communication

// ❌ Anti-pattern: Effect to notify parent
useEffect(() => {
  onChange(internalValue);
}, [internalValue, onChange]);

// ✅ Correct: Call during event
const handleChange = (value: string) => {
  setInternalValue(value);
  onChange(value);
};

4. Initialization from Props

// ❌ Anti-pattern: Sync props to state
const [value, setValue] = useState(initialValue);

useEffect(() => {
  setValue(initialValue);
}, [initialValue]);

// ✅ Correct: Use key to reset
<Editor key={documentId} initialValue={initialValue} />;

Legitimate useEffect Use Cases

Effects are appropriate for:

  1. External system synchronization (DOM manipulation, third-party libraries)
  2. Subscriptions (WebSocket, event listeners, observers)
  3. Data fetching (when not using a data library)
// ✅ Legitimate: External system sync
useEffect(() => {
  const map = new MapLibrary(mapRef.current);
  map.setCenter(coordinates);

  return () => map.destroy();
}, [coordinates]);

// ✅ Legitimate: Subscription
useEffect(() => {
  const unsubscribe = store.subscribe(handleChange);
  return unsubscribe;
}, []);

Performance Optimization Strategy

Step 1: Measure First

Never optimize without profiling. Use React DevTools Profiler to identify actual bottlenecks.

Step 2: Structural Solutions Before Memoization

Try these before adding useMemo/useCallback:

  • Lift state down: Move state closer to where it's used
  • Extract components: Isolate frequently updating parts
  • Use children prop: Pass static JSX as children

Step 3: Memoize Strategically

When memoization is needed:

// Memoize expensive computations
const sortedData = useMemo(
  () => data.toSorted((a, b) => complexSort(a, b)),
  [data],
);

// Memoize callbacks for optimized children
const handleClick = useCallback(
  (id: string) => dispatch({ type: "SELECT", id }),
  [dispatch],
);

// Combine with React.memo for child optimization
const MemoizedChild = memo(function Child({ onClick }: Props) {
  return <button onClick={onClick}>Click</button>;
});

Custom Hook Design Principles

Single Responsibility

Each custom hook should do one thing well:

// ✅ Focused hooks
function useLocalStorage<T>(key: string, initial: T) {
  /* ... */
}
function useDebounce<T>(value: T, delay: number) {
  /* ... */
}
function useMediaQuery(query: string) {
  /* ... */
}

Return Type Patterns

PatternUse WhenExample
Tuple [value, setter]State-like APIuseState, useLocalStorage
Object { data, error, loading }Multiple related valuesuseFetch, useQuery
Single valueRead-only derived datauseMediaQuery, useOnline

Composition Over Complexity

Build complex behavior from simple hooks:

function useSearchWithDebounce(initialQuery: string) {
  const [query, setQuery] = useState(initialQuery);
  const debouncedQuery = useDebounce(query, 300);
  const results = useSearch(debouncedQuery);

  return { query, setQuery, results };
}

Common Mistakes at a Glance

MistakeWhy It's WrongFix
useState + useEffect for filteringExtra render, sync bugsCalculate during render
useMemo(() => CONSTANT, [])Unnecessary overheadModule-level constant
// eslint-disable-next-lineHides real bugsFix the dependency issue
Unstable custom hook returnsBreaks consumer memoizationMemoize all non-primitives
useEffect for analytics on clickDelayed, indirectTrack in click handler
Manual loading/error stateBoilerplate, race conditionsSuspense + ErrorBoundary

Code Review Checklist

When reviewing React hooks code, verify:

Mandatory (Zero Tolerance)

  • No ESLint exhaustive-deps warnings suppressed
  • No useMemo for constant values (use module-level)
  • All custom hook returns are stable (memoized or primitives)

Anti-Patterns

  • No useEffect for derived state calculations
  • No useEffect for event response logic
  • No useState for values computable from other state/props
  • No useMemo/useCallback without proven performance need

Quality

  • Dependencies arrays are complete and accurate
  • Custom hooks follow single responsibility principle
  • Cleanup functions provided where needed
  • Modern hooks used where React version permits

Additional Resources

Reference Files

For detailed guidance and examples, consult:

  • references/unnecessary-hooks.md - Comprehensive patterns for eliminating unnecessary hooks with before/after examples
  • references/custom-hooks.md - Advanced custom hook design patterns and composition strategies
  • references/dependency-array.md - Deep dive into dependency array management and common pitfalls

Example Files

Working examples in examples/:

  • good-patterns.tsx - Correct hook usage examples
  • anti-patterns.tsx - Common mistakes with corrections

Summary

The goal is writing React code that is:

  1. Minimal: Use hooks only when necessary
  2. Direct: Prefer event handlers over effects
  3. Calculated: Derive values during render when possible
  4. Measured: Optimize based on profiling, not assumptions

Apply the principle: "The best hook is the one you don't need to write."

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

コメント

ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。

まだコメントはありません。最初のコメントを書いてみませんか。

送信するとすぐに公開されます。メールアドレスや電話番号などの個人情報は書かないでください。URL は 2 つまでです。

0 / 1000 文字

このフォームは reCAPTCHA で保護されており、Google のプライバシーポリシーと利用規約が適用されます。

同じリポジトリのスキル

Migrate existing ADRs to YAML frontmatter format. Scans docs/decisions/ for ADRs without frontmatter, infers status from body text, shows dry-run for approval. Trigger: /adr-migrate

日本語の概要は準備中です。原文の説明を表示しています。

berlysia/dotfiles92026年10月20日 更新

Start or resume an ADR session. Without number: show status overview and recommend next actionable ADR. With number: provide phase-specific guidance. Trigger: /adr-session, /adr-session NNN, /adr-session new

日本語の概要は準備中です。原文の説明を表示しています。

berlysia/dotfiles92026年10月20日 更新

Analyze a project's harness maturity and guide staged adoption of Harness Engineering best practices (quality loops, safety gates, linters, CLAUDE.md optimization).

日本語の概要は準備中です。原文の説明を表示しています。

berlysia/dotfiles92026年10月20日 更新

bugfix

無料

Standardized bug fix workflow using TDD (Red-Green-Refactor). Use when fixing bugs, resolving issues, or when the user invokes /bugfix. Ensures root cause analysis, failing test first, then minimal fix.

日本語の概要は準備中です。原文の説明を表示しています。

berlysia/dotfiles92026年10月20日 更新

clarify

無料

Create detailed specifications by iteratively clarifying unclear points for Plan mode. Use when: After completing a plan when detailed requirements need clarification before implementation.

日本語の概要は準備中です。原文の説明を表示しています。

berlysia/dotfiles92026年10月20日 更新

Policy-enforcing installer that deploys hardened Claude Code Action workflows (`@claude` manual mention + dependency-bot auto-fix) into a personal GitHub repository the user administers. Use when asked to introduce Claude Code Action, apply `@claude` mention support, or set up automated CI auto-fix for dependency bot PRs, using the security-hardened templates shipped with this skill.

日本語の概要は準備中です。原文の説明を表示しています。

berlysia/dotfiles92026年10月20日 更新

berlysia のスキルをすべて見る

このスキルの問題を報告する