chore: publish from staged

This commit is contained in:
github-actions[bot]
2026-04-09 06:26:21 +00:00
parent 017f31f495
commit a68b190031
467 changed files with 97527 additions and 276 deletions

View File

@@ -0,0 +1,116 @@
# Context File Template
Standard template for a new context module. Copy and fill in the name.
## Template
```jsx
// src/contexts/[Name]Context.js
import React from 'react';
// ─── 1. Default Value ───────────────────────────────────────────────────────
// Shape must match what the provider will pass as `value`
// Used when a consumer renders outside any provider (edge case protection)
const defaultValue = {
// fill in the shape
};
// ─── 2. Create Context ──────────────────────────────────────────────────────
export const [Name]Context = React.createContext(defaultValue);
// ─── 3. Display Name (for React DevTools) ───────────────────────────────────
[Name]Context.displayName = '[Name]Context';
// ─── 4. Optional: Custom Hook (strongly recommended) ────────────────────────
// Provides a clean import path and a helpful error if used outside provider
export function use[Name]() {
const context = React.useContext([Name]Context);
if (context === defaultValue) {
// Only throw if defaultValue is a sentinel - skip if a real default makes sense
// throw new Error('use[Name] must be used inside a [Name]Provider');
}
return context;
}
```
## Filled Example - AuthContext
```jsx
// src/contexts/AuthContext.js
import React from 'react';
const defaultValue = {
user: null,
isAuthenticated: false,
login: () => Promise.resolve(),
logout: () => {},
};
export const AuthContext = React.createContext(defaultValue);
AuthContext.displayName = 'AuthContext';
export function useAuth() {
return React.useContext(AuthContext);
}
```
## Filled Example - ThemeContext
```jsx
// src/contexts/ThemeContext.js
import React from 'react';
const defaultValue = {
theme: 'light',
toggleTheme: () => {},
};
export const ThemeContext = React.createContext(defaultValue);
ThemeContext.displayName = 'ThemeContext';
export function useTheme() {
return React.useContext(ThemeContext);
}
```
## Where to Put Context Files
```
src/
contexts/ ← preferred: dedicated folder
AuthContext.js
ThemeContext.js
```
Alternative acceptable locations:
```
src/context/ ← singular is also fine
src/store/contexts/ ← if co-located with state management
```
Do NOT put context files inside a component folder - contexts are cross-cutting and shouldn't be owned by any one component.
## Provider Placement in the App
Context providers wrap the components that need access. Place as low in the tree as possible, not always at root:
```jsx
// App.js
import { ThemeProvider } from './ThemeProvider';
import { AuthProvider } from './AuthProvider';
function App() {
return (
// Auth wraps everything - login state is needed everywhere
<AuthProvider>
{/* Theme wraps only the UI shell - not needed in pure data providers */}
<ThemeProvider>
<Router>
<AppShell />
</Router>
</ThemeProvider>
</AuthProvider>
);
}
```

View File

@@ -0,0 +1,195 @@
# Multiple Legacy Contexts - Migration Reference
## Identifying Multiple Contexts
A React 16/17 codebase often has several legacy contexts used for different concerns:
```bash
# Find distinct context names used in childContextTypes
grep -rn "childContextTypes" src/ --include="*.js" --include="*.jsx" | grep -v "\.test\."
# Each hit is a separate context to migrate
```
Common patterns in class-heavy codebases:
- **Theme context** - dark/light mode, color palette
- **Auth context** - current user, login/logout functions
- **Router context** - current route, navigation (if using older react-router)
- **Store context** - Redux store, dispatch (if using older connect patterns)
- **Locale/i18n context** - language, translation function
- **Toast/notification context** - show/hide notifications
---
## Migration Order
Migrate contexts one at a time. Each is an independent migration:
```
For each legacy context:
1. Create src/contexts/[Name]Context.js
2. Update the provider
3. Update all consumers
4. Run the app - verify no warning for this context
5. Move to the next context
```
Do not migrate all providers first then all consumers - it leaves the app in a broken intermediate state.
---
## Multiple Contexts in the Same Provider
Some apps combined multiple contexts in one provider component:
```jsx
// Before - one provider exports multiple context values:
class AppProvider extends React.Component {
static childContextTypes = {
theme: PropTypes.string,
user: PropTypes.object,
locale: PropTypes.string,
notifications: PropTypes.array,
};
getChildContext() {
return {
theme: this.state.theme,
user: this.state.user,
locale: this.state.locale,
notifications: this.state.notifications,
};
}
}
```
**Migration approach - split into separate contexts:**
```jsx
// src/contexts/ThemeContext.js
export const ThemeContext = React.createContext('light');
// src/contexts/AuthContext.js
export const AuthContext = React.createContext({ user: null, login: () => {}, logout: () => {} });
// src/contexts/LocaleContext.js
export const LocaleContext = React.createContext('en');
// src/contexts/NotificationContext.js
export const NotificationContext = React.createContext([]);
```
```jsx
// AppProvider.js - now wraps with multiple providers
import { ThemeContext } from './contexts/ThemeContext';
import { AuthContext } from './contexts/AuthContext';
import { LocaleContext } from './contexts/LocaleContext';
import { NotificationContext } from './contexts/NotificationContext';
class AppProvider extends React.Component {
render() {
const { theme, user, locale, notifications } = this.state;
return (
<ThemeContext.Provider value={theme}>
<AuthContext.Provider value={{ user, login: this.login, logout: this.logout }}>
<LocaleContext.Provider value={locale}>
<NotificationContext.Provider value={notifications}>
{this.props.children}
</NotificationContext.Provider>
</LocaleContext.Provider>
</AuthContext.Provider>
</ThemeContext.Provider>
);
}
}
```
---
## Consumer With Multiple Contexts (Class Component)
Class components can only use ONE `static contextType`. For multiple, use `Consumer` render props or convert to a function component.
### Option A - Render Props (keep as class component)
```jsx
import { ThemeContext } from '../contexts/ThemeContext';
import { AuthContext } from '../contexts/AuthContext';
class UserPanel extends React.Component {
render() {
return (
<ThemeContext.Consumer>
{(theme) => (
<AuthContext.Consumer>
{({ user, logout }) => (
<div className={`panel panel-${theme}`}>
<span>{user?.name}</span>
<button onClick={logout}>Sign out</button>
</div>
)}
</AuthContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
}
```
### Option B - Convert to Function Component (preferred)
```jsx
import { useContext } from 'react';
import { ThemeContext } from '../contexts/ThemeContext';
import { AuthContext } from '../contexts/AuthContext';
function UserPanel() {
const theme = useContext(ThemeContext);
const { user, logout } = useContext(AuthContext);
return (
<div className={`panel panel-${theme}`}>
<span>{user?.name}</span>
<button onClick={logout}>Sign out</button>
</div>
);
}
```
If converting to a function component is out of scope for this migration sprint - use Option A. If the class component is simple (mostly just render), Option B is worth the minor rewrite.
---
## Context File Naming Conventions
Use consistent naming across the codebase:
```
src/
contexts/
ThemeContext.js → exports: ThemeContext, ThemeProvider (optional)
AuthContext.js → exports: AuthContext, AuthProvider (optional)
LocaleContext.js → exports: LocaleContext
```
Each file exports the context object. The provider can stay in its original file and just import the context.
---
## Verification After All Contexts Migrated
```bash
# Should return zero hits for legacy context patterns
echo "=== childContextTypes ==="
grep -rn "childContextTypes" src/ --include="*.js" --include="*.jsx" | grep -v "\.test\." | wc -l
echo "=== contextTypes (legacy) ==="
grep -rn "^\s*static contextTypes\s*=\|contextTypes\.propTypes" src/ --include="*.js" | grep -v "\.test\." | wc -l
echo "=== getChildContext ==="
grep -rn "getChildContext" src/ --include="*.js" --include="*.jsx" | grep -v "\.test\." | wc -l
echo "All three should be 0"
```
Note: `static contextType` (singular) is the MODERN API - that's correct. Only `contextTypes` (plural) is legacy.

View File

@@ -0,0 +1,224 @@
# 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 (
<button className={`btn btn-${theme}`} onClick={toggleTheme}>
Toggle Theme
</button>
);
}
}
```
**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: <ThemeContext value={...}>
// React 18: <ThemeContext.Provider value={...}>
return (
<ThemeContext.Provider
value={{
theme: this.state.theme,
toggleTheme: this.toggleTheme,
}}
>
{this.props.children}
</ThemeContext.Provider>
);
}
}
export default ThemeProvider;
```
> **React 19 note:** In React 19 you can write `<ThemeContext value={...}>` directly (no `.Provider`). For React 18.3.1 use `<ThemeContext.Provider value={...}>`.
---
### 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 (
<button className={`btn btn-${theme}`} onClick={toggleTheme}>
Toggle Theme
</button>
);
}
}
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 <h1 className={`header-${theme}`}>{title}</h1>;
}
```
---
### 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 (
<ThemeContext.Consumer>
{({ theme }) => (
<AuthContext.Consumer>
{({ user }) => (
<div className={`dashboard-${theme}`}>
Welcome, {user.name}
</div>
)}
</AuthContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
}
```
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.