agentleFS
Sagargupta16Sagargupta16/agent-recipes/skills/class-to-hooks/SKILL.md

class-to-hooks

Skill4 starsChanged 8 months agoMITView on GitHub

Convert React class components to modern functional components with hooks while preserving all behavior, lifecycle logic, and edge cases. Use it when modernizing a React codebase that uses class components; when a class component needs new features that are easier to implement with hooks; when you want to reduce bundle size (functional components are smaller); when preparing for concurrent React features that work better with functional components.

We split this skill into the rules it gives an agent

11 rules. About 0 are common, 7 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈7

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If multiple state values always change together, keep them in a single useState with an object

    line 17
  2. Very specific:

    If state updates depend on previous state, always use the functional form: setState(prev => ...)

    line 18
  3. Very specific:

    Only use useCallback when the function is passed to a memoized child component or used as a useEffect dependency.

    line 50
  4. Distinctive:

    If the component is an error boundary, either keep it as a class or extract the error boundary into a separate class wrapper and convert …

    line 59
  5. Very specific:

    If the class component has methods called by parent via ref, use useImperativeHandle

    line 69
  6. Distinctive:

    Do NOT change the component's external API (props, ref interface, context)

    line 72
  7. Distinctive:

    Do NOT combine unrelated state into one useState call

    line 73
  8. Distinctive:

    Do NOT add unnecessary useMemo or useCallback (premature optimization)

    line 74
  9. Distinctive:

    Do NOT change the rendering logic or JSX structure

    line 75
  10. Distinctive:

    Do NOT fix bugs during migration (note them as TODOs instead)

    line 76
  11. Distinctive:

    Do NOT change the file's export pattern (default vs.

    line 77

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md
<!-- Generated from recipes/migrations/class-to-hooks.md by scripts/build-skills.sh. Edit the recipe, then rerun the script. -->

React Class Components to Hooks Migration

You are a React migration expert. Convert the provided React class component(s) to functional components with hooks. The converted component must be behaviorally identical to the original, including all lifecycle timing, error handling, and edge cases.

CONVERSION RULES

State

  • this.state = { ... } -> useState() for each independent piece of state
  • If multiple state values always change together, keep them in a single useState with an object
  • If state updates depend on previous state, always use the functional form: setState(prev => ...)
  • this.setState callback (second argument) -> use useEffect that depends on the state value

Lifecycle Methods

Class MethodHooks Equivalent
constructoruseState initial values + top-level statements
componentDidMountuseEffect(() => { ... }, [])
componentDidUpdateuseEffect(() => { ... }, [deps]) or useEffect(() => { ... })
componentWillUnmountuseEffect(() => { return () => cleanup }, [])
shouldComponentUpdateReact.memo() wrapper with custom comparison
getDerivedStateFromPropsCompute during render (variable assignment) or useState + update in render
getSnapshotBeforeUpdateuseRef + useLayoutEffect (rare, flag if needed)
componentDidCatchKeep as class component (error boundaries require classes) OR use a library like react-error-boundary

Refs

  • createRef() -> useRef()
  • Callback refs -> useCallback ref pattern
  • String refs (legacy) -> useRef()
  • this.myRef.current -> myRef.current (no change in access pattern)

Instance Variables (non-state, non-ref)

  • this.intervalId, this.abortController, etc. -> useRef() (mutable ref that doesn't trigger re-renders)
  • These are NOT state because changing them should NOT cause a re-render

Context

  • static contextType = MyContext -> useContext(MyContext)
  • Multiple contexts: nest useContext calls (no need for nested Consumer components)

Event Handlers

  • Class methods -> functions defined inside the component or wrapped in useCallback if passed as props to child components
  • Only use useCallback when the function is passed to a memoized child component or used as a useEffect dependency. Don't wrap every handler in useCallback

Performance

  • PureComponent -> React.memo(Component)
  • shouldComponentUpdate(nextProps, nextState) -> React.memo(Component, (prevProps, nextProps) => { ... }) (note: return true to SKIP re-render, opposite of shouldComponentUpdate)
  • Expensive calculations in render -> useMemo(() => compute(deps), [deps])

Error Boundaries

  • Error boundaries CANNOT be converted to functional components (React limitation as of React 18)
  • If the component is an error boundary, either keep it as a class or extract the error boundary into a separate class wrapper and convert only the rendering logic to a functional component
  • Alternatively, suggest using the react-error-boundary library

IMPORTANT: Preserve These Behaviors

  • Preserve the exact same props interface (don't rename or restructure props)
  • Preserve forwardRef if the component accepts refs
  • Preserve displayName if set
  • Preserve default props (convert static defaultProps to default parameter values)
  • Preserve the component's position in the render tree (don't wrap in extra divs)
  • Preserve all event handler behavior and side effects
  • If the class component has methods called by parent via ref, use useImperativeHandle

What NOT to Do

  • Do NOT change the component's external API (props, ref interface, context)
  • Do NOT combine unrelated state into one useState call
  • Do NOT add unnecessary useMemo or useCallback (premature optimization)
  • Do NOT change the rendering logic or JSX structure
  • Do NOT fix bugs during migration (note them as TODOs instead)
  • Do NOT change the file's export pattern (default vs. named)

OUTPUT FORMAT

Provide:

  1. The complete converted functional component
  2. A migration notes section listing:
    • Each lifecycle method and its hooks equivalent
    • Any behavioral nuances to verify in testing
    • Any changes that couldn't be made (e.g., error boundaries)
    • Suggested follow-up refactors (but not done in this migration)

The source recipe, with a worked example and customization tips, is https://github.com/Sagargupta16/agent-recipes/blob/main/recipes/migrations/class-to-hooks.md

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.