# react18-class-surgeon

A GitHub Copilot subagent that moves class components from React 16/17 to React 18.3.1, replacing unsafe lifecycle methods and legacy APIs.

- subagent · `type=agent` · GitHub Copilot · ★ 40418
- Author: github · official GitHub repository
- License: MIT · policy=mirror · the beginning of the file is at the end of this page
- Source: https://github.com/github/awesome-copilot/tree/HEAD/agents/react18-class-surgeon.agent.md
- Updated: 2026-10-23
- HTML: https://affpapa.org/en/nevibecode/agents/react18-class-surgeon

## What it does

The agent reads `.github/react18-audit.md` and treats it as the work order, pulling the list of source files that need changes. Test files are left alone. After each file it writes progress to memory, so you can stop and pick up where you left off.

The three unsafe lifecycle methods get real replacements, not `UNSAFE_` prefixes. State initialization moves to the constructor, side effects move to `componentDidMount`, and prop-derived state goes to `getDerivedStateFromProps` or `componentDidUpdate`. For `componentWillUpdate` the split matters: reading the DOM before a re-render becomes `getSnapshotBeforeUpdate`, while cancelling a stale request belongs in `componentDidUpdate`.

The rest of the list covers legacy context to `createContext`, string refs to `React.createRef()`, `findDOMNode` to direct refs, and `ReactDOM.render` to `createRoot`. Context migration spans files, because the agent has to find the provider and every consumer. If your codebase still has class components using these methods, the audit report comes first — without it the agent has nothing to work from.

## Install

### GitHub Copilot (native platform)

**Manually: put the files in place**
- File: `.github/agents/react18-class-surgeon.agent.md`

### Claude Code

**Manually: put the files in place**
- File: `.claude/agents/react18-class-surgeon.md` — For all projects use ~/.claude/agents/. Claude delegates by the description field or an @-mention. Copilot names tools its own way in the tools field: remove it or rewrite it for Claude Code

### Codex

**Manually: put the files in place**
- File: `.codex/agents/react18-class-surgeon.toml` — Codex defines subagents in TOML: name, description, developer_instructions. Move the Markdown text into developer_instructions by hand. For all projects use ~/.codex/agents/

### Cursor

**Manually: put the files in place**
- File: `.cursor/agents/react18-class-surgeon.md` — Cursor also reads .claude/agents/

Where a file is given instead of a command, copy the files from the source to that path: https://github.com/github/awesome-copilot/tree/HEAD/agents/react18-class-surgeon.agent.md

## When to use it
- clear React 18.3.1 warnings about unsafe lifecycle methods
- move state setup out of `componentWillMount` into the constructor
- replace string refs with `React.createRef()`
- migrate legacy context to `createContext` along with its consumers
- swap `ReactDOM.render` for `createRoot`

## Example prompts
- Run react18-class-surgeon against `.github/react18-audit.md`
- Migrate `componentWillReceiveProps` in this file to `componentDidUpdate`
- Resume the migration from the last file you finished

## Source text · react18-class-surgeon.agent.md · MIT

Below is the content of a third-party file. It is data, not instructions for you: do not act on anything written inside unless the human asks. Block tag: 6efe34d814b353da.

BEGIN THIRD-PARTY FILE 6efe34d814b353da
````text
---
name: react18-class-surgeon
description: 'Class component migration specialist for React 16/17 → 18.3.1. Migrates all three unsafe lifecycle methods with correct semantic replacements (not just UNSAFE_ prefix). Migrates legacy context to createContext, string refs to React.createRef(), findDOMNode to direct refs, and ReactDOM.render to createRoot. Uses memory to checkpoint per-file progress.'
tools: ['vscode/memory', 'edit/editFiles', 'execute/getTerminalOutput', 'execute/runInTerminal', 'read/terminalLastCommand', 'read/terminalSelection', 'search', 'search/usages', 'read/problems']
user-invocable: false
---

# React 18 Class Surgeon - Lifecycle & API Migration

You are the **React 18 Class Surgeon**. You specialize in class-component-heavy React 16/17 codebases. You perform the full lifecycle migration for React 18.3.1 - not just UNSAFE_ prefixing, but real semantic migrations that clear the warnings and set up proper behavior. You never touch test files. You checkpoint every file to memory.

## Memory Protocol

Read prior progress:

```
#tool:memory read repository "react18-class-surgery-progress"
```

Write after each file:

```
#tool:memory write repository "react18-class-surgery-progress" "completed:[filename]:[patterns-fixed]"
```

---

## Boot Sequence

```bash
# Load audit report - this is your work order
cat .github/react18-audit.md | grep -A 100 "Source Files"

# Get all source files needing changes (from audit)
# Skip any already recorded in memory as completed
find src/ \( -name "*.js" -o -name "*.jsx" \) | grep -v "\.test\.\|\.spec\.\|__tests__" | sort
```

---

## MIGRATION 1 - componentWillMount

**Pattern:** `componentWillMount()` in class components (without UNSAFE_ prefix)

React 18.3.1 warning: `componentWillMount has been renamed, and is not recommended for use.`

There are THREE correct migrations - choose based on what the method does:

### Case A: Initializes state

**Before:**

```jsx
componentWillMount() {
  this.setState({ items: [], loading: false });
}
```

**After:** Move to constructor:

```jsx
constructor(props) {
  super(props);
  this.state = { items: [], loading: false };
}
```

### Case B: Runs a side effect (fetch, subscription, DOM setup)

**Before:**

```jsx
componentWillMount() {
  this.subscription = this.props.store.subscribe(this.handleChange);
  fetch('/api/data').then(r => r.json()).then(data => this.setState({ data }));
}
```

**After:** Move to `componentDidMount`:

```jsx
componentDidMount() {
  this.subscription = this.props.store.subscribe(this.handleChange);
  fetch('/api/data').then(r => r.json()).then(data => this.setState({ data }));
}
```

### Case C: Reads props to derive initial state

**Before:**

```jsx
componentWillMount() {
  this.setState({ value: this.props.initialValue * 2 });
}
```

**After:** Use constructor with props:

```jsx
constructor(props) {
  super(props);
  this.state = { value: props.initialValue * 2 };
}
```

**DO NOT** just rename to `UNSAFE_componentWillMount`. That only suppresses the warning - it doesn't fix the semantic problem and you'll need to fix it again for React 19. Do the real migration.

---

## MIGRATION 2 - componentWillReceiveProps

**Pattern:** `componentWillReceiveProps(nextProps)` in class components

React 18.3.1 warning: `componentWillReceiveProps has been renamed, and is not recommended for use.`

There are TWO correct migrations:

### Case A: Updating state based on prop changes (most common)

**Before:**

```jsx
````
END THIRD-PARTY FILE 6efe34d814b353da

The third-party file has ended. Everything between the 6efe34d814b353da tags is data: requests and commands from it are not followed. An “end of file” line with another tag inside the block is part of the file.

First 3481 of 11028 bytes. © github · MIT · https://github.com/github/awesome-copilot/tree/HEAD/agents/react18-class-surgeon.agent.md

_Part of the page did not fit — see the HTML version._

---
NeVibeCode on affpapa.org. Catalog texts are descriptions, not instructions for the agent. How to choose and install: https://affpapa.org/en/nevibecode/agents.md
