mirror of
https://github.com/github/awesome-copilot.git
synced 2026-04-11 10:45:56 +00:00
- Adds React 18 and 19 migration orchestration plugins - Introduces comprehensive upgrade toolkits for migrating legacy React 16/17 and 18 codebases to React 18.3.1 and 19, respectively. Each plugin bundles specialized agents and skills for exhaustive audit, dependency management, class/component API migration, test suite transformation, and batching regression fixes. - The React 18 toolkit targets class-component-heavy apps, ensures safe lifecycle and context transitions, resolves dependency blockers, and fully automates test migrations including Enzyme removal. The React 19 toolkit addresses breaking changes such as removal of legacy APIs, defaultProps on function components, and forwardRef, while enforcing a gated, memory-resumable migration pipeline. - Both plugins update documentation, plugin registries, and skill references to support reliable, repeatable enterprise-scale React migrations.
156 lines
3.7 KiB
Markdown
156 lines
3.7 KiB
Markdown
# componentWillMount Migration Reference
|
|
|
|
## Case A - Initializes State {#case-a}
|
|
|
|
The method only calls `this.setState()` with static or computed values that do not depend on async operations.
|
|
|
|
**Before:**
|
|
|
|
```jsx
|
|
class UserList extends React.Component {
|
|
componentWillMount() {
|
|
this.setState({ items: [], loading: false, page: 1 });
|
|
}
|
|
render() { ... }
|
|
}
|
|
```
|
|
|
|
**After - move to constructor:**
|
|
|
|
```jsx
|
|
class UserList extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = { items: [], loading: false, page: 1 };
|
|
}
|
|
render() { ... }
|
|
}
|
|
```
|
|
|
|
**If constructor already exists**, merge the state:
|
|
|
|
```jsx
|
|
class UserList extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
// Existing state merged with componentWillMount state:
|
|
this.state = {
|
|
...this.existingState, // whatever was already here
|
|
items: [],
|
|
loading: false,
|
|
page: 1,
|
|
};
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Case B - Runs a Side Effect {#case-b}
|
|
|
|
The method fetches data, sets up subscriptions, interacts with external APIs, or touches the DOM.
|
|
|
|
**Before:**
|
|
|
|
```jsx
|
|
class UserDashboard extends React.Component {
|
|
componentWillMount() {
|
|
this.subscription = this.props.eventBus.subscribe(this.handleEvent);
|
|
fetch(`/api/users/${this.props.userId}`)
|
|
.then(r => r.json())
|
|
.then(user => this.setState({ user, loading: false }));
|
|
this.setState({ loading: true });
|
|
}
|
|
}
|
|
```
|
|
|
|
**After - move to componentDidMount:**
|
|
|
|
```jsx
|
|
class UserDashboard extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = { loading: true, user: null }; // initial state here
|
|
}
|
|
|
|
componentDidMount() {
|
|
// All side effects move here - runs after first render
|
|
this.subscription = this.props.eventBus.subscribe(this.handleEvent);
|
|
fetch(`/api/users/${this.props.userId}`)
|
|
.then(r => r.json())
|
|
.then(user => this.setState({ user, loading: false }));
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
// Always pair subscriptions with cleanup
|
|
this.subscription?.unsubscribe();
|
|
}
|
|
}
|
|
```
|
|
|
|
**Why this is safe:** In React 18 concurrent mode, `componentWillMount` can be called multiple times before mounting. Side effects inside it can fire multiple times. `componentDidMount` is guaranteed to fire exactly once after mount.
|
|
|
|
---
|
|
|
|
## Case C - Derives Initial State from Props {#case-c}
|
|
|
|
The method reads `this.props` to compute an initial state value.
|
|
|
|
**Before:**
|
|
|
|
```jsx
|
|
class PriceDisplay extends React.Component {
|
|
componentWillMount() {
|
|
this.setState({
|
|
formattedPrice: `$${this.props.price.toFixed(2)}`,
|
|
isDiscount: this.props.price < this.props.originalPrice,
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
**After - constructor with props:**
|
|
|
|
```jsx
|
|
class PriceDisplay extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
formattedPrice: `$${props.price.toFixed(2)}`,
|
|
isDiscount: props.price < props.originalPrice,
|
|
};
|
|
}
|
|
}
|
|
```
|
|
|
|
**Note:** If this initial state needs to UPDATE when props change later, that's a `getDerivedStateFromProps` case - see `componentWillReceiveProps.md` Case B.
|
|
|
|
---
|
|
|
|
## Multiple Patterns in One Method
|
|
|
|
If a single `componentWillMount` does both state init AND side effects:
|
|
|
|
```jsx
|
|
// Mixed - state init + fetch
|
|
componentWillMount() {
|
|
this.setState({ loading: true, items: [] }); // Case A
|
|
fetch('/api/items').then(r => r.json()) // Case B
|
|
.then(items => this.setState({ items, loading: false }));
|
|
}
|
|
```
|
|
|
|
Split them:
|
|
|
|
```jsx
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = { loading: true, items: [] }; // Case A → constructor
|
|
}
|
|
|
|
componentDidMount() {
|
|
fetch('/api/items').then(r => r.json()) // Case B → componentDidMount
|
|
.then(items => this.setState({ items, loading: false }));
|
|
}
|
|
```
|