mcpField
I
ideal-react-component

ideal-react-component

github.com/antjanusDevelopment281 installsUpdated 2w ago
Repository

About ideal-react-component

A skill that guides React developers in organizing function components into seven sections and avoiding common Hooks antipatterns. It also describes when to extract stateful logic into a custom hook.

Key features

  1. 01Organize code into seven dependency-ordered sections
  2. 02Summarize common Hooks antipatterns
  3. 03Guide component refactoring and hook extraction
  4. 04Explain early returns and hook call order
  5. 05Point to focused reference documents

Use cases

  1. 01Standardize React function component files
  2. 02Investigate repeated renders or stale state
  3. 03Extract complex component logic into a custom hook

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/antjanus/skillbox --skill ideal-react-component

After installing, type/ideal-react-componentin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

reference/
COMPLETE-EXAMPLES.md
EVAL.md
HOOKS-ANTIPATTERNS.md
REFACTORING.md
SECTIONS.md

Details

From the project README

Ideal React Component Structure

Overview

A predictable seven-section order for function-component files — imports → styles → types → component → logic → conditional render → default render — so anyone opening the file knows where to look. Paired with the hooks antipatterns that cause most infinite loops and stale state.

Core principle: the file reads top-to-bottom in dependency order, and the happy path lands at the bottom where it's most visible. It's a pattern, not a law — small components and React Server Components legitimately skip sections.

The seven-section structure

// 1. IMPORTS (grouped: React → third-party → internal @/ → local, blank line between)
import React, { useState, useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import styled from 'styled-components';
import { api } from '@/services/api';
import { Button } from './Button';

// 2. STYLED COMPONENTS (prefix "Styled" so they're instantly recognizable)
const StyledContainer = styled.div`padding: 1rem; background: white;`;

// 3. TYPE DEFINITIONS (ComponentNameProps, declared right above the component)
type UserProfileProps = {
  userId: string;
  onUpdate?: (user: User) => void;
};

// 4. COMPONENT FUNCTION (named export, const arrow function)
export const UserProfile = ({ userId, onUpdate }: UserProfileProps): React.JSX.Element => {
  // 5. LOGIC
  const [isEditing, setIsEditing] = useState(false);                        // 5.1 local state
  const { data, isLoading, error } = useQuery({ queryKey: ['user', userId], queryFn: () => api.getUser(userId) }); // 5.2 custom/data hooks
  useEffect(() => { /* focus the editor */ }, [isEditing]);                 // 5.3 effects
  const displayName = data ? `${data.first} ${data.last}` : '';            // 5.4 post-processing
  const handleEdit = () => setIsEditing(true);                             // 5.5 handlers

  // 6. CONDITIONAL RENDERING (exit early for each edge case, after every hook call)
  if (isLoading) return <Loading />;
  if (error) return <Error message={error.message} />;
  if (!data) return <Empty />;

  // 7. DEFAULT RENDER (success/happy path stays at the bottom — most visible)
  return <StyledContainer>{/* Main JSX */}</StyledContainer>;
};

State first, effects after the hooks they depend on, handlers last — so each line only references things already declared above it.

JavaScript: same pattern without type annotations — skip Section 3 or use JSDoc.

SectionWhat goes hereWhy
1. ImportsReact, libraries, internal @/, localEasy to find dependencies
2. StylingStyled comps (prefix Styled), Tailwind, CSS ModulesVisual separation from logic
3. Types*Props, *Return above the componentAPI visible at a glance
4. Componentexport const Component = (...) =>Named exports refactor cleanly
5. Logicstate → hooks → effects → post-processing → handlersRespects hook rules; deps before dependents
6. Conditional renderEarly returns for loading/error/emptyReduces nesting; types narrow after guards
7. Default renderSuccess-state JSXHappy path is the most visible code

Top hooks antipatterns

The three most frequent causes of infinite loops, stale data, and surprise re-renders. Desired form first.

1. Derive during render; don't sync with an effect. An effect that only computes state from other state costs an extra render pass, and loops outright if the parent feeds the value back down.

const fullName = `${first} ${last}`;                                   // ✅ derive during render
useEffect(() => { setFullName(`${first} ${last}`); }, [first, last]); // ❌ extra render, loop risk

2. Reset prop-derived state with key; don't expect useState to track props. The initializer runs once, so a changed prop leaves the state stale.

<Component key={itemId} initialValue={data.value} />      // ✅ remount resets state
const [value, setValue] = useState(props.initialValue);   // ❌ stale after prop changes

3. List every dependency. Omitting one captures a stale closure — the effect keeps reading the first render's values.

useEffect(() => { setTotal(count * price); }, [count, price]);   // ✅ all deps
useEffect(() => { setTotal(count * price); }, [price]);          // ❌ missing count

Refactoring

When a component passes ~50 lines of logic or ~200 total, extract the stateful logic into a use[Domain] hook — the component becomes presentation-focused and the hook owns state and data flow. Refactor the component the user named; a sibling or parent that has the same problem is a follow-up to report, not a file to touch in the same change. Extraction criteria and composition patterns: reference/REFACTORING.md.

Gotchas

  • Early returns must sit below every hook call. Section 6 comes after Section 5 for a hard reason, not tidiness: a return above a useEffect changes the hook count between renders and React throws "Rendered fewer hooks than expected." If a guard needs to short-circuit expensive work, gate inside the hook instead.
  • Server Components can't hold Sections 2 and 5. In the Next.js App Router a file is a Server Component by default; useState, useEffect, and styled-components all require 'use client' at the top. The build error names the hook, not the missing directive.
  • Object and array literals in dependency arrays loop forever. }, [{ id }]) or }, [items.filter(...)]) allocates a fresh identity every render, so the effect always re-fires. Move the literal inside the effect, or memoize it with useMemo.
  • key-resetting remounts the whole subtree. It discards child state, uncontrolled input values, focus, and running animations. It's the right call for "this is a different record now," the wrong one for a single field that needs re-seeding.
  • Deriving beats memoizing until it measurably doesn't. Reach for useMemo only when the computation is genuinely expensive — a template string or .map() over a short list is cheaper than the memo bookkeeping.
  • Return type is React.JSX.Element, not bare JSX.Element. React 19's types dropped the global JSX namespace, so the unqualified form no longer resolves.
  • @tanstack/react-query v5 takes one object argument. The positional useQuery(key, fn, options) overloads were removed. v5 also renamed the 'loading' status to 'pending' and redefined isLoading as isPending && isFetching, so "no data yet" is now isPending.
  • GSD execution phases don't auto-activate skills. Invoke /ideal-react-component explicitly, or record the convention in the project's CLAUDE.md.

Deep reference

Load one file when the question calls for it — not up front.

LoadWhen
reference/SECTIONS.mdPer-section ✅/❌ detail, styling-solution variants, structural troubleshooting
reference/HOOKS-ANTIPATTERNS.mdFull antipattern set with mechanisms — the top three above are the summary
reference/REFACTORING.mdExtracting and composing custom hooks
reference/COMPLETE-EXAMPLES.mdFull TS and JS components with all seven sections
reference/EVAL.mdActivation eval set (maintainers only)

Source

Comments

Markdown supported