# Single Context Migration - Complete Before/After
## Full Example: ThemeContext
This covers the most common pattern - one context with one provider and multiple consumers.
---
### Step 1 - Before State (Legacy)
**ThemeProvider.js (provider):**
```jsx
import PropTypes from 'prop-types';
class ThemeProvider extends React.Component {
static childContextTypes = {
theme: PropTypes.string,
toggleTheme: PropTypes.func,
};
state = { theme: 'light' };
toggleTheme = () => {
this.setState(s => ({ theme: s.theme === 'light' ? 'dark' : 'light' }));
};
getChildContext() {
return {
theme: this.state.theme,
toggleTheme: this.toggleTheme,
};
}
render() {
return this.props.children;
}
}
```
**ThemedButton.js (class consumer):**
```jsx
import PropTypes from 'prop-types';
class ThemedButton extends React.Component {
static contextTypes = {
theme: PropTypes.string,
toggleTheme: PropTypes.func,
};
render() {
const { theme, toggleTheme } = this.context;
return (
);
}
}
```
**ThemedHeader.js (function consumer - if any):**
```jsx
// Function components couldn't use legacy context cleanly
// They had to use a class wrapper or render prop
```
---
### Step 2 - Create Context File
**src/contexts/ThemeContext.js (new file):**
```jsx
import React from 'react';
// Default value matches the shape of getChildContext() return
export const ThemeContext = React.createContext({
theme: 'light',
toggleTheme: () => {},
});
// Named export for the context - both provider and consumers import from here
```
---
### Step 3 - Update Provider
**ThemeProvider.js (after):**
```jsx
import React from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
class ThemeProvider extends React.Component {
state = { theme: 'light' };
toggleTheme = () => {
this.setState(s => ({ theme: s.theme === 'light' ? 'dark' : 'light' }));
};
render() {
// React 19 JSX shorthand:
// React 18:
return (
{this.props.children}
);
}
}
export default ThemeProvider;
```
> **React 19 note:** In React 19 you can write `` directly (no `.Provider`). For React 18.3.1 use ``.
---
### Step 4 - Update Class Consumer
**ThemedButton.js (after):**
```jsx
import React from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
class ThemedButton extends React.Component {
// singular contextType (not contextTypes)
static contextType = ThemeContext;
render() {
const { theme, toggleTheme } = this.context;
return (
);
}
}
export default ThemedButton;
```
**Key differences from legacy:**
- `static contextType` (singular) not `contextTypes` (plural)
- No PropTypes declaration needed
- `this.context` is the full value object (not a partial - whatever you passed to `value`)
- Only ONE context per class component via `contextType` - use `Context.Consumer` render prop for multiple
---
### Step 5 - Update Function Consumer
**ThemedHeader.js (after - now straightforward with hooks):**
```jsx
import { useContext } from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
function ThemedHeader({ title }) {
const { theme } = useContext(ThemeContext);
return
{title}
;
}
```
---
### Step 6 - Multiple Contexts in One Class Component
If a class component consumed more than one legacy context, it gets complex. Class components can only have one `static contextType`. For multiple contexts, use the render prop form:
```jsx
import { ThemeContext } from '../contexts/ThemeContext';
import { AuthContext } from '../contexts/AuthContext';
class Dashboard extends React.Component {
render() {
return (
{({ theme }) => (
{({ user }) => (
Welcome, {user.name}
)}
)}
);
}
}
```
Or consider migrating the class component to a function component to use `useContext` cleanly.
---
### Verification Checklist
After migrating one context:
```bash
# Provider - no legacy context exports remain
grep -n "childContextTypes\|getChildContext" src/ThemeProvider.js
# Consumers - no legacy context consumption remains
grep -rn "contextTypes\s*=" src/ --include="*.js" --include="*.jsx" | grep -v "ThemeContext\|\.test\."
# this.context usage - confirm it reads from contextType not legacy
grep -rn "this\.context\." src/ --include="*.js" | grep -v "\.test\."
```
Each should return zero hits for the migrated context.