React 18 lifecycle patterns
Classify the semantics of unsafe class lifecycle methods before changing code, then apply the matching React 18.3.1 migration pattern from the bundled references.
When to invoke
- "Migrate
componentWillMount in this component."
- "Fix React 18 UNSAFE lifecycle warnings."
- "Should this use
getDerivedStateFromProps or componentDidUpdate?"
- "Add
getSnapshotBeforeUpdate for scroll position."
- "Replace
componentWillReceiveProps safely."
Procedure
- Identify which unsafe lifecycle appears:
componentWillMount, componentWillReceiveProps, or componentWillUpdate.
- Classify what the method does semantically before editing.
- Read the matching bundled reference file and case section.
- Apply the before/after pattern exactly enough to preserve behavior.
- Avoid permanent
UNSAFE_ prefixes; if a temporary prefix is unavoidable, add the required TODO.
- Validate behavior with existing React tests or targeted component checks.
Quick decision guide
componentWillMount
| What it does |
Correct migration |
Reference |
Sets initial state with this.setState(...) |
Move to constructor. |
references/componentWillMount.md#case-a |
| Runs a side effect such as fetch, subscription, or DOM access |
Move to componentDidMount. |
references/componentWillMount.md#case-b |
| Derives initial state from props |
Move to constructor with props. |
references/componentWillMount.md#case-c |
componentWillReceiveProps
| What it does |
Correct migration |
Reference |
| Async side effect triggered by prop change, such as fetch or cancel |
Use componentDidUpdate with previous-prop comparison. |
references/componentWillReceiveProps.md#case-a |
| Pure state derivation from new props with no side effects |
Use getDerivedStateFromProps. |
references/componentWillReceiveProps.md#case-b |
componentWillUpdate
| What it does |
Correct migration |
Reference |
| Reads DOM state before update, such as scroll, size, or position |
Use getSnapshotBeforeUpdate and consume the snapshot in componentDidUpdate. |
references/componentWillUpdate.md#case-a |
| Cancels requests or runs effects before update |
Use componentDidUpdate with previous-value comparison. |
references/componentWillUpdate.md#case-b |
UNSAFE prefix rule
Never use UNSAFE_componentWillMount, UNSAFE_componentWillReceiveProps, or UNSAFE_componentWillUpdate as a permanent fix. Prefixing suppresses React 18.3.1 warnings but does not fix concurrent mode safety issues, does not prepare for React 19 removal, and does not address the semantic migration.
If a temporary prefix is explicitly chosen as a stopgap, mark it with this exact intent:
// TODO: React 19 will remove this. Migrate before React 19 upgrade.
// UNSAFE_ prefix added temporarily - replace with componentDidMount / getDerivedStateFromProps / etc.
Progressive disclosure and bundled resources
references/componentWillMount.md: three cases for initial state, side effects, and props-derived initial state.
references/componentWillReceiveProps.md: componentDidUpdate versus getDerivedStateFromProps, including trap warnings.
references/componentWillUpdate.md: getSnapshotBeforeUpdate plus componentDidUpdate pairing.
Gotchas
- Wrong category means wrong migration: a side effect in
componentWillReceiveProps belongs in componentDidUpdate, not getDerivedStateFromProps.
getSnapshotBeforeUpdate is paired: DOM snapshots are returned before commit and consumed in componentDidUpdate.
UNSAFE_ is not a migration: treat it only as a temporary scheduling marker.
Output template
## React lifecycle migration
**Status:** complete | needs changes | blocked
**Component:** `<component name or path>`
**Original lifecycle:** `componentWillMount` | `componentWillReceiveProps` | `componentWillUpdate`
**Migration pattern:** `constructor` | `componentDidMount` | `componentDidUpdate` | `getDerivedStateFromProps` | `getSnapshotBeforeUpdate`
| Behavior found | Pattern applied | Reference | Validation |
| --- | --- | --- | --- |
| <state, side effect, DOM read, or cancel> | <new lifecycle/API> | `references/<file>.md#case-x` | <test/check> |
Quality gate
1---2name: react18-lifecycle-patterns3description: Migrate unsafe React class component lifecycle methods to React 18.3.1-safe patterns. Use when fixing `componentWillMount`, `componentWillReceiveProps`, `componentWillUpdate`, `UNSAFE_` lifecycle warnings, choosing `getDerivedStateFromProps` versus `componentDidUpdate`, or adding `getSnapshotBeforeUpdate`.4---56<!-- Generated from harness/github-copilot/skills/react18-lifecycle-patterns/SKILL.md by harness/claude-code/scripts/convert_from_copilot.py. Edit the source, not this file. -->78# React 18 lifecycle patterns910Classify the semantics of unsafe class lifecycle methods before changing code, then apply the matching React 18.3.1 migration pattern from the bundled references.1112## When to invoke1314- "Migrate `componentWillMount` in this component."15- "Fix React 18 UNSAFE lifecycle warnings."16- "Should this use `getDerivedStateFromProps` or `componentDidUpdate`?"17- "Add `getSnapshotBeforeUpdate` for scroll position."18- "Replace `componentWillReceiveProps` safely."1920## Procedure21221. Identify which unsafe lifecycle appears: `componentWillMount`, `componentWillReceiveProps`, or `componentWillUpdate`.232. Classify what the method does semantically before editing.243. Read the matching bundled reference file and case section.254. Apply the before/after pattern exactly enough to preserve behavior.265. Avoid permanent `UNSAFE_` prefixes; if a temporary prefix is unavoidable, add the required TODO.276. Validate behavior with existing React tests or targeted component checks.2829## Quick decision guide3031### componentWillMount3233| What it does | Correct migration | Reference |34| --- | --- | --- |35| Sets initial state with `this.setState(...)` | Move to `constructor`. | `references/componentWillMount.md#case-a` |36| Runs a side effect such as fetch, subscription, or DOM access | Move to `componentDidMount`. | `references/componentWillMount.md#case-b` |37| Derives initial state from props | Move to `constructor` with props. | `references/componentWillMount.md#case-c` |3839### componentWillReceiveProps4041| What it does | Correct migration | Reference |42| --- | --- | --- |43| Async side effect triggered by prop change, such as fetch or cancel | Use `componentDidUpdate` with previous-prop comparison. | `references/componentWillReceiveProps.md#case-a` |44| Pure state derivation from new props with no side effects | Use `getDerivedStateFromProps`. | `references/componentWillReceiveProps.md#case-b` |4546### componentWillUpdate4748| What it does | Correct migration | Reference |49| --- | --- | --- |50| Reads DOM state before update, such as scroll, size, or position | Use `getSnapshotBeforeUpdate` and consume the snapshot in `componentDidUpdate`. | `references/componentWillUpdate.md#case-a` |51| Cancels requests or runs effects before update | Use `componentDidUpdate` with previous-value comparison. | `references/componentWillUpdate.md#case-b` |5253## UNSAFE prefix rule5455Never use `UNSAFE_componentWillMount`, `UNSAFE_componentWillReceiveProps`, or `UNSAFE_componentWillUpdate` as a permanent fix. Prefixing suppresses React 18.3.1 warnings but does not fix concurrent mode safety issues, does not prepare for React 19 removal, and does not address the semantic migration.5657If a temporary prefix is explicitly chosen as a stopgap, mark it with this exact intent:5859```jsx60// TODO: React 19 will remove this. Migrate before React 19 upgrade.61// UNSAFE_ prefix added temporarily - replace with componentDidMount / getDerivedStateFromProps / etc.62```6364## Progressive disclosure and bundled resources6566- `references/componentWillMount.md`: three cases for initial state, side effects, and props-derived initial state.67- `references/componentWillReceiveProps.md`: `componentDidUpdate` versus `getDerivedStateFromProps`, including trap warnings.68- `references/componentWillUpdate.md`: `getSnapshotBeforeUpdate` plus `componentDidUpdate` pairing.6970## Gotchas7172- **Wrong category means wrong migration**: a side effect in `componentWillReceiveProps` belongs in `componentDidUpdate`, not `getDerivedStateFromProps`.73- **`getSnapshotBeforeUpdate` is paired**: DOM snapshots are returned before commit and consumed in `componentDidUpdate`.74- **`UNSAFE_` is not a migration**: treat it only as a temporary scheduling marker.7576## Output template7778```markdown79## React lifecycle migration8081**Status:** complete | needs changes | blocked82**Component:** `<component name or path>`83**Original lifecycle:** `componentWillMount` | `componentWillReceiveProps` | `componentWillUpdate`84**Migration pattern:** `constructor` | `componentDidMount` | `componentDidUpdate` | `getDerivedStateFromProps` | `getSnapshotBeforeUpdate`8586| Behavior found | Pattern applied | Reference | Validation |87| --- | --- | --- | --- |88| <state, side effect, DOM read, or cancel> | <new lifecycle/API> | `references/<file>.md#case-x` | <test/check> |89```9091## Quality gate9293- [ ] The original lifecycle method and semantic category were identified before editing.94- [ ] The matching bundled reference file was consulted.95- [ ] `componentWillMount`, `componentWillReceiveProps`, and `componentWillUpdate` are removed or explicitly justified as temporary `UNSAFE_` stopgaps.96- [ ] `getDerivedStateFromProps` is used only for pure state derivation from props.97- [ ] DOM reads before update use `getSnapshotBeforeUpdate` with `componentDidUpdate`.98- [ ] Existing tests or targeted checks validate preserved behavior.