diff --git a/packages/react-devtools-shared/src/backend/ReactSymbols.js b/packages/react-devtools-shared/src/backend/ReactSymbols.js index 64f84cc913705..2a79ce83ae0a4 100644 --- a/packages/react-devtools-shared/src/backend/ReactSymbols.js +++ b/packages/react-devtools-shared/src/backend/ReactSymbols.js @@ -51,6 +51,8 @@ export const PROFILER_SYMBOL_STRING = 'Symbol(react.profiler)'; export const PROVIDER_NUMBER = 0xeacd; export const PROVIDER_SYMBOL_STRING = 'Symbol(react.provider)'; +export const CONSUMER_SYMBOL_STRING = 'Symbol(react.consumer)'; + export const SCOPE_NUMBER = 0xead7; export const SCOPE_SYMBOL_STRING = 'Symbol(react.scope)'; diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js index 069cde215a1ca..59b2059dc8a61 100644 --- a/packages/react-devtools-shared/src/backend/renderer.js +++ b/packages/react-devtools-shared/src/backend/renderer.js @@ -79,6 +79,7 @@ import { PROVIDER_SYMBOL_STRING, CONTEXT_NUMBER, CONTEXT_SYMBOL_STRING, + CONSUMER_SYMBOL_STRING, STRICT_MODE_NUMBER, STRICT_MODE_SYMBOL_STRING, PROFILER_NUMBER, @@ -525,6 +526,15 @@ export function getInternalReactConstants(version: string): { case CONTEXT_NUMBER: case CONTEXT_SYMBOL_STRING: case SERVER_CONTEXT_SYMBOL_STRING: + if ( + fiber.type._context === undefined && + fiber.type.Provider === fiber.type + ) { + // In 19+, Context.Provider === Context, so this is a provider. + resolvedContext = fiber.type; + return `${resolvedContext.displayName || 'Context'}.Provider`; + } + // 16.3-16.5 read from "type" because the Consumer is the actual context object. // 16.6+ should read from "type._context" because Consumer can be different (in DEV). // NOTE Keep in sync with inspectElementRaw() @@ -533,6 +543,10 @@ export function getInternalReactConstants(version: string): { // NOTE: TraceUpdatesBackendManager depends on the name ending in '.Consumer' // If you change the name, figure out a more resilient way to detect it. return `${resolvedContext.displayName || 'Context'}.Consumer`; + case CONSUMER_SYMBOL_STRING: + // 19+ + resolvedContext = fiber.type._context; + return `${resolvedContext.displayName || 'Context'}.Consumer`; case STRICT_MODE_NUMBER: case STRICT_MODE_SYMBOL_STRING: return null; @@ -3178,8 +3192,14 @@ export function attach( } } } else if ( - typeSymbol === CONTEXT_NUMBER || - typeSymbol === CONTEXT_SYMBOL_STRING + // Detect pre-19 Context Consumers + (typeSymbol === CONTEXT_NUMBER || typeSymbol === CONTEXT_SYMBOL_STRING) && + !( + // In 19+, CONTEXT_SYMBOL_STRING means a Provider instead. + // It will be handled in a different branch below. + // Eventually, this entire branch can be removed. + (type._context === undefined && type.Provider === type) + ) ) { // 16.3-16.5 read from "type" because the Consumer is the actual context object. // 16.6+ should read from "type._context" because Consumer can be different (in DEV). @@ -3209,6 +3229,35 @@ export function attach( } } + current = current.return; + } + } else if ( + // Detect 19+ Context Consumers + typeSymbol === CONSUMER_SYMBOL_STRING + ) { + // This branch is 19+ only, where Context.Provider === Context. + // NOTE Keep in sync with getDisplayNameForFiber() + const consumerResolvedContext = type._context; + + // Global context value. + context = consumerResolvedContext._currentValue || null; + + // Look for overridden value. + let current = ((fiber: any): Fiber).return; + while (current !== null) { + const currentType = current.type; + const currentTypeSymbol = getTypeSymbol(currentType); + if ( + // In 19+, these are Context Providers + currentTypeSymbol === CONTEXT_SYMBOL_STRING + ) { + const providerResolvedContext = currentType; + if (providerResolvedContext === consumerResolvedContext) { + context = current.memoizedProps.value; + break; + } + } + current = current.return; } }