{"kind":"Skill","metadata":{"namespace":"community","name":"react18-legacy-context","version":"0.1.0"},"spec":{"description":"Provides the complete migration pattern for React legacy context API (contextTypes, childContextTypes, getChildContext) to the modern createContext API. Use this skill whenever migrating legacy context in class components - this is always a cross-file migration requiring the provider AND all consumers to be updated together. Use it before touching any contextTypes or childContextTypes code, because migrating only the provider without the consumers (or vice versa) will cause a runtime failure. Always read this skill before writing any context migration - the cross-file coordination steps here prevent the most common context migration bugs.","files":{"SKILL.md":"---\nname: react18-legacy-context\ndescription: 'Provides the complete migration pattern for React legacy context API (contextTypes, childContextTypes, getChildContext) to the modern createContext API. Use this skill whenever migrating legacy context in class components - this is always a cross-file migration requiring the provider AND all consumers to be updated together. Use it before touching any contextTypes or childContextTypes code, because migrating only the provider without the consumers (or vice versa) will cause a runtime failure. Always read this skill before writing any context migration - the cross-file coordination steps here prevent the most common context migration bugs.'\n---\n\n# React 18 Legacy Context Migration\n\nLegacy context (`contextTypes`, `childContextTypes`, `getChildContext`) was deprecated in React 16.3 and warns in React 18.3.1. It is **removed in React 19**.\n\n## This Is Always a Cross-File Migration\n\nUnlike most other migrations that touch one file at a time, context migration requires coordinating:\n1. Create the context object (usually a new file)\n2. Update the **provider** component\n3. Update **every consumer** component\n\nMissing any consumer leaves the app broken - it will read from the wrong context or get `undefined`.\n\n## Migration Steps (Always Follow This Order)\n\n```\nStep 1: Find the provider (childContextTypes + getChildContext)\nStep 2: Find ALL consumers (contextTypes)\nStep 3: Create the context file\nStep 4: Update the provider\nStep 5: Update each consumer (class components → contextType, function components → useContext)\nStep 6: Verify - run the app, check no legacy context warnings remain\n```\n\n## Scan Commands\n\n```bash\n# Find all providers\ngrep -rn \"childContextTypes\\|getChildContext\" src/ --include=\"*.js\" --include=\"*.jsx\" | grep -v \"\\.test\\.\"\n\n# Find all consumers\ngrep -rn \"contextTypes\\s*=\" src/ --include=\"*.js\" --include=\"*.jsx\" | grep -v \"\\.test\\.\"\n\n# Find this.context usage (may be legacy or modern - check which)\ngrep -rn \"this\\.context\\.\" src/ --include=\"*.js\" --include=\"*.jsx\" | grep -v \"\\.test\\.\"\n```\n\n## Reference Files\n\n- **`references/single-context.md`** - complete migration for one context (theme, auth, etc.) with provider + class consumer + function consumer\n- **`references/multi-context.md`** - apps with multiple legacy contexts (nested providers, multiple consumers of different contexts)\n- **`references/context-file-template.md`** - the standard file structure for a new context module\n","references/context-file-template.md":"# Context File Template\n\nStandard template for a new context module. Copy and fill in the name.\n\n## Template\n\n```jsx\n// src/contexts/[Name]Context.js\nimport React from 'react';\n\n// ─── 1. Default Value ───────────────────────────────────────────────────────\n// Shape must match what the provider will pass as `value`\n// Used when a consumer renders outside any provider (edge case protection)\nconst defaultValue = {\n  // fill in the shape\n};\n\n// ─── 2. Create Context ──────────────────────────────────────────────────────\nexport const [Name]Context = React.createContext(defaultValue);\n\n// ─── 3. Display Name (for React DevTools) ───────────────────────────────────\n[Name]Context.displayName = '[Name]Context';\n\n// ─── 4. Optional: Custom Hook (strongly recommended) ────────────────────────\n// Provides a clean import path and a helpful error if used outside provider\nexport function use[Name]() {\n  const context = React.useContext([Name]Context);\n  if (context === defaultValue) {\n    // Only throw if defaultValue is a sentinel - skip if a real default makes sense\n    // throw new Error('use[Name] must be used inside a [Name]Provider');\n  }\n  return context;\n}\n```\n\n## Filled Example - AuthContext\n\n```jsx\n// src/contexts/AuthContext.js\nimport React from 'react';\n\nconst defaultValue = {\n  user: null,\n  isAuthenticated: false,\n  login: () =\u003e Promise.resolve(),\n  logout: () =\u003e {},\n};\n\nexport const AuthContext = React.createContext(defaultValue);\nAuthContext.displayName = 'AuthContext';\n\nexport function useAuth() {\n  return React.useContext(AuthContext);\n}\n```\n\n## Filled Example - ThemeContext\n\n```jsx\n// src/contexts/ThemeContext.js\nimport React from 'react';\n\nconst defaultValue = {\n  theme: 'light',\n  toggleTheme: () =\u003e {},\n};\n\nexport const ThemeContext = React.createContext(defaultValue);\nThemeContext.displayName = 'ThemeContext';\n\nexport function useTheme() {\n  return React.useContext(ThemeContext);\n}\n```\n\n## Where to Put Context Files\n\n```\nsrc/\n  contexts/           ← preferred: dedicated folder\n    AuthContext.js\n    ThemeContext.js\n```\n\nAlternative acceptable locations:\n\n```\nsrc/context/          ← singular is also fine\nsrc/store/contexts/   ← if co-located with state management\n```\n\nDo NOT put context files inside a component folder - contexts are cross-cutting and shouldn't be owned by any one component.\n\n## Provider Placement in the App\n\nContext providers wrap the components that need access. Place as low in the tree as possible, not always at root:\n\n```jsx\n// App.js\nimport { ThemeProvider } from './ThemeProvider';\nimport { AuthProvider } from './AuthProvider';\n\nfunction App() {\n  return (\n    // Auth wraps everything - login state is needed everywhere\n    \u003cAuthProvider\u003e\n      {/* Theme wraps only the UI shell - not needed in pure data providers */}\n      \u003cThemeProvider\u003e\n        \u003cRouter\u003e\n          \u003cAppShell /\u003e\n        \u003c/Router\u003e\n      \u003c/ThemeProvider\u003e\n    \u003c/AuthProvider\u003e\n  );\n}\n```\n","references/multi-context.md":"# Multiple Legacy Contexts - Migration Reference\n\n## Identifying Multiple Contexts\n\nA React 16/17 codebase often has several legacy contexts used for different concerns:\n\n```bash\n# Find distinct context names used in childContextTypes\ngrep -rn \"childContextTypes\" src/ --include=\"*.js\" --include=\"*.jsx\" | grep -v \"\\.test\\.\"\n# Each hit is a separate context to migrate\n```\n\nCommon patterns in class-heavy codebases:\n\n- **Theme context** - dark/light mode, color palette\n- **Auth context** - current user, login/logout functions\n- **Router context** - current route, navigation (if using older react-router)\n- **Store context** - Redux store, dispatch (if using older connect patterns)\n- **Locale/i18n context** - language, translation function\n- **Toast/notification context** - show/hide notifications\n\n---\n\n## Migration Order\n\nMigrate contexts one at a time. Each is an independent migration:\n\n```\nFor each legacy context:\n  1. Create src/contexts/[Name]Context.js\n  2. Update the provider\n  3. Update all consumers\n  4. Run the app - verify no warning for this context\n  5. Move to the next context\n```\n\nDo not migrate all providers first then all consumers - it leaves the app in a broken intermediate state.\n\n---\n\n## Multiple Contexts in the Same Provider\n\nSome apps combined multiple contexts in one provider component:\n\n```jsx\n// Before - one provider exports multiple context values:\nclass AppProvider extends React.Component {\n  static childContextTypes = {\n    theme: PropTypes.string,\n    user: PropTypes.object,\n    locale: PropTypes.string,\n    notifications: PropTypes.array,\n  };\n\n  getChildContext() {\n    return {\n      theme: this.state.theme,\n      user: this.state.user,\n      locale: this.state.locale,\n      notifications: this.state.notifications,\n    };\n  }\n}\n```\n\n**Migration approach - split into separate contexts:**\n\n```jsx\n// src/contexts/ThemeContext.js\nexport const ThemeContext = React.createContext('light');\n\n// src/contexts/AuthContext.js\nexport const AuthContext = React.createContext({ user: null, login: () =\u003e {}, logout: () =\u003e {} });\n\n// src/contexts/LocaleContext.js\nexport const LocaleContext = React.createContext('en');\n\n// src/contexts/NotificationContext.js\nexport const NotificationContext = React.createContext([]);\n```\n\n```jsx\n// AppProvider.js - now wraps with multiple providers\nimport { ThemeContext } from './contexts/ThemeContext';\nimport { AuthContext } from './contexts/AuthContext';\nimport { LocaleContext } from './contexts/LocaleContext';\nimport { NotificationContext } from './contexts/NotificationContext';\n\nclass AppProvider extends React.Component {\n  render() {\n    const { theme, user, locale, notifications } = this.state;\n    return (\n      \u003cThemeContext.Provider value={theme}\u003e\n        \u003cAuthContext.Provider value={{ user, login: this.login, logout: this.logout }}\u003e\n          \u003cLocaleContext.Provider value={locale}\u003e\n            \u003cNotificationContext.Provider value={notifications}\u003e\n              {this.props.children}\n            \u003c/NotificationContext.Provider\u003e\n          \u003c/LocaleContext.Provider\u003e\n        \u003c/AuthContext.Provider\u003e\n      \u003c/ThemeContext.Provider\u003e\n    );\n  }\n}\n```\n\n---\n\n## Consumer With Multiple Contexts (Class Component)\n\nClass components can only use ONE `static contextType`. For multiple, use `Consumer` render props or convert to a function component.\n\n### Option A - Render Props (keep as class component)\n\n```jsx\nimport { ThemeContext } from '../contexts/ThemeContext';\nimport { AuthContext } from '../contexts/AuthContext';\n\nclass UserPanel extends React.Component {\n  render() {\n    return (\n      \u003cThemeContext.Consumer\u003e\n        {(theme) =\u003e (\n          \u003cAuthContext.Consumer\u003e\n            {({ user, logout }) =\u003e (\n              \u003cdiv className={`panel panel-${theme}`}\u003e\n                \u003cspan\u003e{user?.name}\u003c/span\u003e\n                \u003cbutton onClick={logout}\u003eSign out\u003c/button\u003e\n              \u003c/div\u003e\n            )}\n          \u003c/AuthContext.Consumer\u003e\n        )}\n      \u003c/ThemeContext.Consumer\u003e\n    );\n  }\n}\n```\n\n### Option B - Convert to Function Component (preferred)\n\n```jsx\nimport { useContext } from 'react';\nimport { ThemeContext } from '../contexts/ThemeContext';\nimport { AuthContext } from '../contexts/AuthContext';\n\nfunction UserPanel() {\n  const theme = useContext(ThemeContext);\n  const { user, logout } = useContext(AuthContext);\n\n  return (\n    \u003cdiv className={`panel panel-${theme}`}\u003e\n      \u003cspan\u003e{user?.name}\u003c/span\u003e\n      \u003cbutton onClick={logout}\u003eSign out\u003c/button\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\nIf 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.\n\n---\n\n## Context File Naming Conventions\n\nUse consistent naming across the codebase:\n\n```\nsrc/\n  contexts/\n    ThemeContext.js      → exports: ThemeContext, ThemeProvider (optional)\n    AuthContext.js       → exports: AuthContext, AuthProvider (optional)\n    LocaleContext.js     → exports: LocaleContext\n```\n\nEach file exports the context object. The provider can stay in its original file and just import the context.\n\n---\n\n## Verification After All Contexts Migrated\n\n```bash\n# Should return zero hits for legacy context patterns\necho \"=== childContextTypes ===\"\ngrep -rn \"childContextTypes\" src/ --include=\"*.js\" --include=\"*.jsx\" | grep -v \"\\.test\\.\" | wc -l\n\necho \"=== contextTypes (legacy) ===\"\ngrep -rn \"^\\s*static contextTypes\\s*=\\|contextTypes\\.propTypes\" src/ --include=\"*.js\" | grep -v \"\\.test\\.\" | wc -l\n\necho \"=== getChildContext ===\"\ngrep -rn \"getChildContext\" src/ --include=\"*.js\" --include=\"*.jsx\" | grep -v \"\\.test\\.\" | wc -l\n\necho \"All three should be 0\"\n```\n\nNote: `static contextType` (singular) is the MODERN API - that's correct. Only `contextTypes` (plural) is legacy.\n","references/single-context.md":"# Single Context Migration - Complete Before/After\n\n## Full Example: ThemeContext\n\nThis covers the most common pattern - one context with one provider and multiple consumers.\n\n---\n\n### Step 1 - Before State (Legacy)\n\n**ThemeProvider.js (provider):**\n\n```jsx\nimport PropTypes from 'prop-types';\n\nclass ThemeProvider extends React.Component {\n  static childContextTypes = {\n    theme: PropTypes.string,\n    toggleTheme: PropTypes.func,\n  };\n\n  state = { theme: 'light' };\n\n  toggleTheme = () =\u003e {\n    this.setState(s =\u003e ({ theme: s.theme === 'light' ? 'dark' : 'light' }));\n  };\n\n  getChildContext() {\n    return {\n      theme: this.state.theme,\n      toggleTheme: this.toggleTheme,\n    };\n  }\n\n  render() {\n    return this.props.children;\n  }\n}\n```\n\n**ThemedButton.js (class consumer):**\n\n```jsx\nimport PropTypes from 'prop-types';\n\nclass ThemedButton extends React.Component {\n  static contextTypes = {\n    theme: PropTypes.string,\n    toggleTheme: PropTypes.func,\n  };\n\n  render() {\n    const { theme, toggleTheme } = this.context;\n    return (\n      \u003cbutton className={`btn btn-${theme}`} onClick={toggleTheme}\u003e\n        Toggle Theme\n      \u003c/button\u003e\n    );\n  }\n}\n```\n\n**ThemedHeader.js (function consumer - if any):**\n\n```jsx\n// Function components couldn't use legacy context cleanly\n// They had to use a class wrapper or render prop\n```\n\n---\n\n### Step 2 - Create Context File\n\n**src/contexts/ThemeContext.js (new file):**\n\n```jsx\nimport React from 'react';\n\n// Default value matches the shape of getChildContext() return\nexport const ThemeContext = React.createContext({\n  theme: 'light',\n  toggleTheme: () =\u003e {},\n});\n\n// Named export for the context - both provider and consumers import from here\n```\n\n---\n\n### Step 3 - Update Provider\n\n**ThemeProvider.js (after):**\n\n```jsx\nimport React from 'react';\nimport { ThemeContext } from '../contexts/ThemeContext';\n\nclass ThemeProvider extends React.Component {\n  state = { theme: 'light' };\n\n  toggleTheme = () =\u003e {\n    this.setState(s =\u003e ({ theme: s.theme === 'light' ? 'dark' : 'light' }));\n  };\n\n  render() {\n    // React 19 JSX shorthand: \u003cThemeContext value={...}\u003e\n    // React 18: \u003cThemeContext.Provider value={...}\u003e\n    return (\n      \u003cThemeContext.Provider\n        value={{\n          theme: this.state.theme,\n          toggleTheme: this.toggleTheme,\n        }}\n      \u003e\n        {this.props.children}\n      \u003c/ThemeContext.Provider\u003e\n    );\n  }\n}\n\nexport default ThemeProvider;\n```\n\n\u003e **React 19 note:** In React 19 you can write `\u003cThemeContext value={...}\u003e` directly (no `.Provider`). For React 18.3.1 use `\u003cThemeContext.Provider value={...}\u003e`.\n\n---\n\n### Step 4 - Update Class Consumer\n\n**ThemedButton.js (after):**\n\n```jsx\nimport React from 'react';\nimport { ThemeContext } from '../contexts/ThemeContext';\n\nclass ThemedButton extends React.Component {\n  // singular contextType (not contextTypes)\n  static contextType = ThemeContext;\n\n  render() {\n    const { theme, toggleTheme } = this.context;\n    return (\n      \u003cbutton className={`btn btn-${theme}`} onClick={toggleTheme}\u003e\n        Toggle Theme\n      \u003c/button\u003e\n    );\n  }\n}\n\nexport default ThemedButton;\n```\n\n**Key differences from legacy:**\n\n- `static contextType` (singular) not `contextTypes` (plural)\n- No PropTypes declaration needed\n- `this.context` is the full value object (not a partial - whatever you passed to `value`)\n- Only ONE context per class component via `contextType` - use `Context.Consumer` render prop for multiple\n\n---\n\n### Step 5 - Update Function Consumer\n\n**ThemedHeader.js (after - now straightforward with hooks):**\n\n```jsx\nimport { useContext } from 'react';\nimport { ThemeContext } from '../contexts/ThemeContext';\n\nfunction ThemedHeader({ title }) {\n  const { theme } = useContext(ThemeContext);\n  return \u003ch1 className={`header-${theme}`}\u003e{title}\u003c/h1\u003e;\n}\n```\n\n---\n\n### Step 6 - Multiple Contexts in One Class Component\n\nIf 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:\n\n```jsx\nimport { ThemeContext } from '../contexts/ThemeContext';\nimport { AuthContext } from '../contexts/AuthContext';\n\nclass Dashboard extends React.Component {\n  render() {\n    return (\n      \u003cThemeContext.Consumer\u003e\n        {({ theme }) =\u003e (\n          \u003cAuthContext.Consumer\u003e\n            {({ user }) =\u003e (\n              \u003cdiv className={`dashboard-${theme}`}\u003e\n                Welcome, {user.name}\n              \u003c/div\u003e\n            )}\n          \u003c/AuthContext.Consumer\u003e\n        )}\n      \u003c/ThemeContext.Consumer\u003e\n    );\n  }\n}\n```\n\nOr consider migrating the class component to a function component to use `useContext` cleanly.\n\n---\n\n### Verification Checklist\n\nAfter migrating one context:\n\n```bash\n# Provider - no legacy context exports remain\ngrep -n \"childContextTypes\\|getChildContext\" src/ThemeProvider.js\n\n# Consumers - no legacy context consumption remains\ngrep -rn \"contextTypes\\s*=\" src/ --include=\"*.js\" --include=\"*.jsx\" | grep -v \"ThemeContext\\|\\.test\\.\"\n\n# this.context usage - confirm it reads from contextType not legacy\ngrep -rn \"this\\.context\\.\" src/ --include=\"*.js\" | grep -v \"\\.test\\.\"\n```\n\nEach should return zero hits for the migrated context.\n"},"import":{"commit_sha":"541b7819d8c3545c6df122491af4fa1eae415779","imported_at":"2026-05-18T20:05:35Z","license_text":"MIT License\n\nCopyright GitHub, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.","owner":"github","repo":"github/awesome-copilot","source_url":"https://github.com/github/awesome-copilot/tree/541b7819d8c3545c6df122491af4fa1eae415779/plugins/react18-upgrade/skills/react18-legacy-context"}},"content_hash":[192,232,109,202,119,52,54,65,108,81,98,91,160,152,56,75,197,142,191,12,106,56,246,225,36,234,24,85,135,182,154,120],"trust_level":"unsigned","yanked":false}
