From b122abd5be9756c0472652def599214b131d96f4 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Sun, 11 Feb 2024 01:31:39 +0000 Subject: [PATCH] Add a feature flag for new behavior --- .../src/ReactFlightReplyClient.js | 7 +- .../react-debug-tools/src/ReactDebugHooks.js | 6 +- ...eactDOMServerIntegrationNewContext-test.js | 7 ++ .../__tests__/ReactServerRendering-test.js | 1 + packages/react-is/src/ReactIs.js | 28 ++++++- packages/react-reconciler/src/ReactFiber.js | 27 +++++-- .../src/ReactFiberBeginWork.js | 30 +++++-- .../src/ReactFiberCompleteWork.js | 8 +- .../src/ReactFiberNewContext.js | 8 +- .../react-reconciler/src/ReactFiberScope.js | 10 ++- .../src/ReactFiberUnwindWork.js | 15 +++- .../src/__tests__/ReactNewContext-test.js | 2 + packages/react-server/src/ReactFizzServer.js | 39 +++++++++- packages/react/src/ReactContext.js | 78 +++++++++++++++++-- .../__tests__/ReactContextValidator-test.js | 1 + packages/shared/ReactFeatureFlags.js | 2 + packages/shared/ReactSymbols.js | 1 + .../forks/ReactFeatureFlags.native-fb.js | 1 + .../forks/ReactFeatureFlags.native-oss.js | 1 + .../forks/ReactFeatureFlags.test-renderer.js | 1 + .../ReactFeatureFlags.test-renderer.native.js | 1 + .../ReactFeatureFlags.test-renderer.www.js | 1 + .../forks/ReactFeatureFlags.www-dynamic.js | 1 + .../shared/forks/ReactFeatureFlags.www.js | 1 + packages/shared/getComponentNameFromType.js | 28 ++++++- packages/shared/isValidElementType.js | 3 + 26 files changed, 272 insertions(+), 36 deletions(-) diff --git a/packages/react-client/src/ReactFlightReplyClient.js b/packages/react-client/src/ReactFlightReplyClient.js index 6bf4e11a43d53..0e4dfb047ff90 100644 --- a/packages/react-client/src/ReactFlightReplyClient.js +++ b/packages/react-client/src/ReactFlightReplyClient.js @@ -14,11 +14,13 @@ import type { RejectedThenable, ReactCustomFormAction, } from 'shared/ReactTypes'; +import {enableRenderableContext} from 'shared/ReactFeatureFlags'; import { REACT_ELEMENT_TYPE, REACT_LAZY_TYPE, REACT_CONTEXT_TYPE, + REACT_PROVIDER_TYPE, getIteratorFn, } from 'shared/ReactSymbols'; @@ -297,7 +299,10 @@ export function processReply( 'React Lazy cannot be passed to Server Functions from the Client.%s', describeObjectForErrorMessage(parent, key), ); - } else if ((value: any).$$typeof === REACT_CONTEXT_TYPE) { + } else if ( + (value: any).$$typeof === + (enableRenderableContext ? REACT_CONTEXT_TYPE : REACT_PROVIDER_TYPE) + ) { console.error( 'React Context Providers cannot be passed to Server Functions from the Client.%s', describeObjectForErrorMessage(parent, key), diff --git a/packages/react-debug-tools/src/ReactDebugHooks.js b/packages/react-debug-tools/src/ReactDebugHooks.js index 23229dd57c4ff..31308c73ca2e7 100644 --- a/packages/react-debug-tools/src/ReactDebugHooks.js +++ b/packages/react-debug-tools/src/ReactDebugHooks.js @@ -821,7 +821,11 @@ function setupContexts(contextMap: Map, any>, fiber: Fiber) { let current: null | Fiber = fiber; while (current) { if (current.tag === ContextProvider) { - const context: ReactContext = current.type; + let context: ReactContext = current.type; + if ((context: any)._context !== undefined) { + // Support inspection of pre-19+ providers. + context = (context: any)._context; + } if (!contextMap.has(context)) { // Store the current value that we're going to restore later. contextMap.set(context, context._currentValue); diff --git a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js index 1c3a91d160b68..87f5ad625b19e 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerIntegrationNewContext-test.js @@ -296,6 +296,13 @@ describe('ReactDOMServerIntegration', () => { }); itRenders('should treat Context as Context.Provider', async render => { + // The `itRenders` helpers don't work with the gate pragma, so we have to do + // this instead. + if (gate(flags => !flags.enableRenderableContext)) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + const Theme = React.createContext('dark'); const Language = React.createContext('french'); diff --git a/packages/react-dom/src/__tests__/ReactServerRendering-test.js b/packages/react-dom/src/__tests__/ReactServerRendering-test.js index 42a0952350785..2c0d589df35ce 100644 --- a/packages/react-dom/src/__tests__/ReactServerRendering-test.js +++ b/packages/react-dom/src/__tests__/ReactServerRendering-test.js @@ -1000,6 +1000,7 @@ describe('ReactDOMServer', () => { ]); }); + // @gate enableRenderableContext it('should warn if an invalid contextType is defined', () => { const Context = React.createContext(); class ComponentA extends React.Component { diff --git a/packages/react-is/src/ReactIs.js b/packages/react-is/src/ReactIs.js index 9541ed854b8e8..ef4d383b36788 100644 --- a/packages/react-is/src/ReactIs.js +++ b/packages/react-is/src/ReactIs.js @@ -18,12 +18,14 @@ import { REACT_MEMO_TYPE, REACT_PORTAL_TYPE, REACT_PROFILER_TYPE, + REACT_PROVIDER_TYPE, REACT_CONSUMER_TYPE, REACT_STRICT_MODE_TYPE, REACT_SUSPENSE_TYPE, REACT_SUSPENSE_LIST_TYPE, } from 'shared/ReactSymbols'; import isValidElementType from 'shared/isValidElementType'; +import {enableRenderableContext} from 'shared/ReactFeatureFlags'; export function typeOf(object: any): mixed { if (typeof object === 'object' && object !== null) { @@ -49,6 +51,12 @@ export function typeOf(object: any): mixed { case REACT_MEMO_TYPE: case REACT_CONSUMER_TYPE: return $$typeofType; + case REACT_PROVIDER_TYPE: + if (enableRenderableContext) { + // Fallthrough to default + } else { + return $$typeofType; + } default: return $$typeof; } @@ -61,8 +69,12 @@ export function typeOf(object: any): mixed { return undefined; } -export const ContextConsumer = REACT_CONSUMER_TYPE; -export const ContextProvider = REACT_CONTEXT_TYPE; +export const ContextConsumer: symbol = enableRenderableContext + ? REACT_CONSUMER_TYPE + : REACT_CONTEXT_TYPE; +export const ContextProvider: symbol = enableRenderableContext + ? REACT_CONTEXT_TYPE + : REACT_PROVIDER_TYPE; export const Element = REACT_ELEMENT_TYPE; export const ForwardRef = REACT_FORWARD_REF_TYPE; export const Fragment = REACT_FRAGMENT_TYPE; @@ -77,10 +89,18 @@ export const SuspenseList = REACT_SUSPENSE_LIST_TYPE; export {isValidElementType}; export function isContextConsumer(object: any): boolean { - return typeOf(object) === REACT_CONSUMER_TYPE; + if (enableRenderableContext) { + return typeOf(object) === REACT_CONSUMER_TYPE; + } else { + return typeOf(object) === REACT_CONTEXT_TYPE; + } } export function isContextProvider(object: any): boolean { - return typeOf(object) === REACT_CONTEXT_TYPE; + if (enableRenderableContext) { + return typeOf(object) === REACT_CONTEXT_TYPE; + } else { + return typeOf(object) === REACT_PROVIDER_TYPE; + } } export function isElement(object: any): boolean { return ( diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js index 77eb247f95231..a45d7c2661ccd 100644 --- a/packages/react-reconciler/src/ReactFiber.js +++ b/packages/react-reconciler/src/ReactFiber.js @@ -38,6 +38,7 @@ import { enableDebugTracing, enableFloat, enableDO_NOT_USE_disableStrictPassiveEffect, + enableRenderableContext, } from 'shared/ReactFeatureFlags'; import {NoFlags, Placement, StaticMask} from './ReactFiberFlags'; import {ConcurrentRoot} from './ReactRootTags'; @@ -94,6 +95,7 @@ import { REACT_DEBUG_TRACING_MODE_TYPE, REACT_STRICT_MODE_TYPE, REACT_PROFILER_TYPE, + REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE, REACT_CONSUMER_TYPE, REACT_SUSPENSE_TYPE, @@ -578,13 +580,28 @@ export function createFiberFromTypeAndProps( default: { if (typeof type === 'object' && type !== null) { switch (type.$$typeof) { + case REACT_PROVIDER_TYPE: + if (enableRenderableContext) { + // Fallthrough to error + } else { + fiberTag = ContextProvider; + break getTag; + } case REACT_CONTEXT_TYPE: - fiberTag = ContextProvider; - break getTag; + if (enableRenderableContext) { + fiberTag = ContextProvider; + break getTag; + } else { + fiberTag = ContextConsumer; + break getTag; + } case REACT_CONSUMER_TYPE: - // This is a consumer - fiberTag = ContextConsumer; - break getTag; + if (enableRenderableContext) { + fiberTag = ContextConsumer; + break getTag; + } else { + // Fallthrough to error + } case REACT_FORWARD_REF_TYPE: fiberTag = ForwardRef; if (__DEV__) { diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.js b/packages/react-reconciler/src/ReactFiberBeginWork.js index 54d4ee67136e8..94749f8181cee 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.js @@ -110,6 +110,7 @@ import { enableFormActions, enableAsyncActions, enablePostpone, + enableRenderableContext, } from 'shared/ReactFeatureFlags'; import isArray from 'shared/isArray'; import shallowEqual from 'shared/shallowEqual'; @@ -3528,7 +3529,12 @@ function updateContextProvider( workInProgress: Fiber, renderLanes: Lanes, ) { - const context: ReactContext = workInProgress.type; + let context: ReactContext; + if (enableRenderableContext) { + context = workInProgress.type; + } else { + context = workInProgress.type._context; + } const newProps = workInProgress.pendingProps; const oldProps = workInProgress.memoizedProps; @@ -3590,9 +3596,18 @@ function updateContextConsumer( workInProgress: Fiber, renderLanes: Lanes, ) { - const consumerType: ReactConsumerType = workInProgress.type; - const context = consumerType._context; - + let context: ReactContext; + if (enableRenderableContext) { + const consumerType: ReactConsumerType = workInProgress.type; + context = consumerType._context; + } else { + context = workInProgress.type; + if (__DEV__) { + if ((context: any)._context !== undefined) { + context = (context: any)._context; + } + } + } const newProps = workInProgress.pendingProps; const render = newProps.children; @@ -3838,7 +3853,12 @@ function attemptEarlyBailoutIfNoScheduledUpdate( break; case ContextProvider: { const newValue = workInProgress.memoizedProps.value; - const context: ReactContext = workInProgress.type; + let context: ReactContext; + if (enableRenderableContext) { + context = workInProgress.type; + } else { + context = workInProgress.type._context; + } pushProvider(workInProgress, context, newValue); break; } diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js index 9a4269af8f38d..639cec6cbf0d5 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js @@ -39,6 +39,7 @@ import { enableCache, enableTransitionTracing, enableFloat, + enableRenderableContext, passChildrenWhenCloningPersistedNodes, } from 'shared/ReactFeatureFlags'; @@ -1505,7 +1506,12 @@ function completeWork( return null; case ContextProvider: // Pop provider fiber - const context: ReactContext = workInProgress.type; + let context: ReactContext; + if (enableRenderableContext) { + context = workInProgress.type; + } else { + context = workInProgress.type._context; + } popProvider(context, workInProgress); bubbleProperties(workInProgress); return null; diff --git a/packages/react-reconciler/src/ReactFiberNewContext.js b/packages/react-reconciler/src/ReactFiberNewContext.js index 3c1ff79e25149..5d9b165635a1e 100644 --- a/packages/react-reconciler/src/ReactFiberNewContext.js +++ b/packages/react-reconciler/src/ReactFiberNewContext.js @@ -46,6 +46,7 @@ import { enableLazyContextPropagation, enableFormActions, enableAsyncActions, + enableRenderableContext, } from 'shared/ReactFeatureFlags'; import { getHostTransitionProvider, @@ -561,7 +562,12 @@ function propagateParentContextChanges( const oldProps = currentParent.memoizedProps; if (oldProps !== null) { - const context: ReactContext = parent.type; + let context: ReactContext; + if (enableRenderableContext) { + context = parent.type; + } else { + context = parent.type._context; + } const newProps = parent.pendingProps; const newValue = newProps.value; diff --git a/packages/react-reconciler/src/ReactFiberScope.js b/packages/react-reconciler/src/ReactFiberScope.js index 8f9f1cdea577c..0cb1c62ba8d02 100644 --- a/packages/react-reconciler/src/ReactFiberScope.js +++ b/packages/react-reconciler/src/ReactFiberScope.js @@ -22,7 +22,10 @@ import { import {isFiberSuspenseAndTimedOut} from './ReactFiberTreeReflection'; import {HostComponent, ScopeComponent, ContextProvider} from './ReactWorkTags'; -import {enableScopeAPI} from 'shared/ReactFeatureFlags'; +import { + enableScopeAPI, + enableRenderableContext, +} from 'shared/ReactFeatureFlags'; function getSuspenseFallbackChild(fiber: Fiber): Fiber | null { return ((((fiber.child: any): Fiber).sibling: any): Fiber).child; @@ -113,7 +116,10 @@ function collectNearestContextValues( context: ReactContext, childContextValues: Array, ): void { - if (node.tag === ContextProvider && node.type === context) { + if ( + node.tag === ContextProvider && + (enableRenderableContext ? node.type : node.type._context) === context + ) { const contextValue = node.memoizedProps.value; childContextValues.push(contextValue); } else { diff --git a/packages/react-reconciler/src/ReactFiberUnwindWork.js b/packages/react-reconciler/src/ReactFiberUnwindWork.js index d9a9eb5c77860..cc77647cb8556 100644 --- a/packages/react-reconciler/src/ReactFiberUnwindWork.js +++ b/packages/react-reconciler/src/ReactFiberUnwindWork.js @@ -35,6 +35,7 @@ import { enableProfilerTimer, enableCache, enableTransitionTracing, + enableRenderableContext, } from 'shared/ReactFeatureFlags'; import {popHostContainer, popHostContext} from './ReactFiberHostContext'; @@ -160,7 +161,12 @@ function unwindWork( popHostContainer(workInProgress); return null; case ContextProvider: - const context: ReactContext = workInProgress.type; + let context: ReactContext; + if (enableRenderableContext) { + context = workInProgress.type; + } else { + context = workInProgress.type._context; + } popProvider(context, workInProgress); return null; case OffscreenComponent: @@ -250,7 +256,12 @@ function unwindInterruptedWork( popSuspenseListContext(interruptedWork); break; case ContextProvider: - const context: ReactContext = interruptedWork.type; + let context: ReactContext; + if (enableRenderableContext) { + context = interruptedWork.type; + } else { + context = interruptedWork.type._context; + } popProvider(context, interruptedWork); break; case OffscreenComponent: diff --git a/packages/react-reconciler/src/__tests__/ReactNewContext-test.js b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js index a7be10d04436f..91b89bcd7dc1d 100644 --- a/packages/react-reconciler/src/__tests__/ReactNewContext-test.js +++ b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js @@ -1339,6 +1339,7 @@ describe('ReactNewContext', () => { ); }); + // @gate enableRenderableContext it('warns when passed a consumer', async () => { const Context = React.createContext(0); function Foo() { @@ -1635,6 +1636,7 @@ Context fuzz tester error! Copy and paste the following line into the test suite }); }); + // @gate enableRenderableContext it('should treat Context as Context.Provider', async () => { const BarContext = React.createContext({value: 'bar-initial'}); expect(BarContext.Provider).toBe(BarContext); diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index d76b906ff7494..99ea5b4008bd4 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -32,6 +32,7 @@ import type {ContextSnapshot} from './ReactFizzNewContext'; import type {ComponentStackNode} from './ReactFizzComponentStack'; import type {TreeContext} from './ReactFizzTreeContext'; import type {ThenableState} from './ReactFizzThenable'; +import {enableRenderableContext} from 'shared/ReactFeatureFlags'; import { scheduleWork, @@ -127,6 +128,7 @@ import { REACT_FRAGMENT_TYPE, REACT_FORWARD_REF_TYPE, REACT_MEMO_TYPE, + REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE, REACT_CONSUMER_TYPE, REACT_SCOPE_TYPE, @@ -1702,7 +1704,17 @@ function renderContextConsumer( type: ReactConsumerType, props: Object, ): void { - const context = type._context; + let context: ReactContext; + if (enableRenderableContext) { + context = type._context; + } else { + context = (type: any); + if (__DEV__) { + if ((context: any)._context !== undefined) { + context = (context: any)._context; + } + } + } const render = props.children; if (__DEV__) { @@ -1732,6 +1744,9 @@ function renderContextProvider( context: ReactContext, props: Object, ): void { + if (!enableRenderableContext) { + context = (context: any)._context; + } const value = props.value; const children = props.children; let prevSnapshot; @@ -1882,13 +1897,29 @@ function renderElement( renderMemo(request, task, keyPath, type, props, ref); return; } + case REACT_PROVIDER_TYPE: { + if (enableRenderableContext) { + // Fallthrough to error + } else { + renderContextProvider(request, task, keyPath, type, props); + return; + } + } case REACT_CONTEXT_TYPE: { - renderContextProvider(request, task, keyPath, type, props); + if (enableRenderableContext) { + renderContextProvider(request, task, keyPath, type, props); + } else { + renderContextConsumer(request, task, keyPath, type, props); + } return; } case REACT_CONSUMER_TYPE: { - renderContextConsumer(request, task, keyPath, type, props); - return; + if (enableRenderableContext) { + renderContextConsumer(request, task, keyPath, type, props); + return; + } else { + // Fallthrough to error + } } case REACT_LAZY_TYPE: { renderLazyComponent(request, task, keyPath, type, props); diff --git a/packages/react/src/ReactContext.js b/packages/react/src/ReactContext.js index 5f4fc2585c2b7..24461ebfbb7bf 100644 --- a/packages/react/src/ReactContext.js +++ b/packages/react/src/ReactContext.js @@ -7,9 +7,14 @@ * @flow */ -import {REACT_CONSUMER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols'; +import { + REACT_PROVIDER_TYPE, + REACT_CONSUMER_TYPE, + REACT_CONTEXT_TYPE, +} from 'shared/ReactSymbols'; import type {ReactContext} from 'shared/ReactTypes'; +import {enableRenderableContext} from 'shared/ReactFeatureFlags'; export function createContext(defaultValue: T): ReactContext { // TODO: Second argument used to be an optional `calculateChangedBits` @@ -32,11 +37,72 @@ export function createContext(defaultValue: T): ReactContext { Consumer: (null: any), }; - context.Provider = context; - context.Consumer = { - $$typeof: REACT_CONSUMER_TYPE, - _context: context, - }; + if (enableRenderableContext) { + context.Provider = context; + context.Consumer = { + $$typeof: REACT_CONSUMER_TYPE, + _context: context, + }; + } else { + (context: any).Provider = { + $$typeof: REACT_PROVIDER_TYPE, + _context: context, + }; + if (__DEV__) { + const Consumer: any = { + $$typeof: REACT_CONTEXT_TYPE, + _context: context, + }; + Object.defineProperties(Consumer, { + Provider: { + get() { + return context.Provider; + }, + set(_Provider: any) { + context.Provider = _Provider; + }, + }, + _currentValue: { + get() { + return context._currentValue; + }, + set(_currentValue: T) { + context._currentValue = _currentValue; + }, + }, + _currentValue2: { + get() { + return context._currentValue2; + }, + set(_currentValue2: T) { + context._currentValue2 = _currentValue2; + }, + }, + _threadCount: { + get() { + return context._threadCount; + }, + set(_threadCount: number) { + context._threadCount = _threadCount; + }, + }, + Consumer: { + get() { + return context.Consumer; + }, + }, + displayName: { + get() { + return context.displayName; + }, + set(displayName: void | string) {}, + }, + }); + (context: any).Consumer = Consumer; + } else { + (context: any).Consumer = context; + } + } if (__DEV__) { context._currentRenderer = null; diff --git a/packages/react/src/__tests__/ReactContextValidator-test.js b/packages/react/src/__tests__/ReactContextValidator-test.js index 2e3eaf6e28306..160528cde810c 100644 --- a/packages/react/src/__tests__/ReactContextValidator-test.js +++ b/packages/react/src/__tests__/ReactContextValidator-test.js @@ -564,6 +564,7 @@ describe('ReactContextValidator', () => { ); }); + // @gate enableRenderableContext it('should warn if an invalid contextType is defined', () => { const Context = React.createContext(); class ComponentA extends React.Component { diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 3d8a7cf563bc8..c2ffe2a652692 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -121,6 +121,8 @@ export const passChildrenWhenCloningPersistedNodes = false; export const enableUseDeferredValueInitialArg = __EXPERIMENTAL__; +export const enableRenderableContext = false; + /** * Enables an expiration time for retry lanes to avoid starvation. */ diff --git a/packages/shared/ReactSymbols.js b/packages/shared/ReactSymbols.js index 8f7de70ea2ad7..6dca3476be945 100644 --- a/packages/shared/ReactSymbols.js +++ b/packages/shared/ReactSymbols.js @@ -17,6 +17,7 @@ export const REACT_PORTAL_TYPE: symbol = Symbol.for('react.portal'); export const REACT_FRAGMENT_TYPE: symbol = Symbol.for('react.fragment'); export const REACT_STRICT_MODE_TYPE: symbol = Symbol.for('react.strict_mode'); export const REACT_PROFILER_TYPE: symbol = Symbol.for('react.profiler'); +export const REACT_PROVIDER_TYPE: symbol = Symbol.for('react.provider'); // TODO: Delete with enableRenderableContext export const REACT_CONSUMER_TYPE: symbol = Symbol.for('react.consumer'); export const REACT_CONTEXT_TYPE: symbol = Symbol.for('react.context'); export const REACT_FORWARD_REF_TYPE: symbol = Symbol.for('react.forward_ref'); diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 497a5b6bcf3a6..3990e11a2ac9b 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -66,6 +66,7 @@ export const enableComponentStackLocations = false; export const enableLegacyFBSupport = false; export const enableFilterEmptyStringAttributesDOM = true; export const enableGetInspectorDataForInstanceInProduction = true; +export const enableRenderableContext = false; export const enableRetryLaneExpiration = false; export const retryLaneExpirationMs = 5000; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index 67b220974b517..d29f699abbbde 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -49,6 +49,7 @@ export const enableComponentStackLocations = false; export const enableLegacyFBSupport = false; export const enableFilterEmptyStringAttributesDOM = true; export const enableGetInspectorDataForInstanceInProduction = false; +export const enableRenderableContext = false; export const enableRetryLaneExpiration = false; export const retryLaneExpirationMs = 5000; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 63025e79fb18b..6577d2a338893 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -49,6 +49,7 @@ export const enableComponentStackLocations = true; export const enableLegacyFBSupport = false; export const enableFilterEmptyStringAttributesDOM = true; export const enableGetInspectorDataForInstanceInProduction = false; +export const enableRenderableContext = false; export const enableRetryLaneExpiration = false; export const retryLaneExpirationMs = 5000; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js index 4baed7ddd2b07..fc53c654b16d9 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.native.js @@ -51,6 +51,7 @@ export const enableUseEffectEventHook = false; export const enableClientRenderFallbackOnTextMismatch = true; export const enableUseRefAccessWarning = false; export const enableInfiniteRenderLoopDetection = false; +export const enableRenderableContext = false; export const enableRetryLaneExpiration = false; export const retryLaneExpirationMs = 5000; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 0105b2bed97c1..8e21c3b1a6286 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -49,6 +49,7 @@ export const enableComponentStackLocations = true; export const enableLegacyFBSupport = false; export const enableFilterEmptyStringAttributesDOM = true; export const enableGetInspectorDataForInstanceInProduction = false; +export const enableRenderableContext = false; export const enableRetryLaneExpiration = false; export const retryLaneExpirationMs = 5000; diff --git a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js index 9b15bddded3fd..cd073c6429982 100644 --- a/packages/shared/forks/ReactFeatureFlags.www-dynamic.js +++ b/packages/shared/forks/ReactFeatureFlags.www-dynamic.js @@ -29,6 +29,7 @@ export const enableFormActions = __VARIANT__; export const alwaysThrottleRetries = __VARIANT__; export const enableDO_NOT_USE_disableStrictPassiveEffect = __VARIANT__; export const enableUseDeferredValueInitialArg = __VARIANT__; +export const enableRenderableContext = __VARIANT__; export const enableRetryLaneExpiration = __VARIANT__; export const retryLaneExpirationMs = 5000; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 3a3f0f86a6719..798134ab70c24 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -37,6 +37,7 @@ export const { syncLaneExpirationMs, transitionLaneExpirationMs, enableInfiniteRenderLoopDetection, + enableRenderableContext, } = dynamicFeatureFlags; // On WWW, __EXPERIMENTAL__ is used for a new modern build. diff --git a/packages/shared/getComponentNameFromType.js b/packages/shared/getComponentNameFromType.js index 390c46ff0a14e..fb63ff465a6af 100644 --- a/packages/shared/getComponentNameFromType.js +++ b/packages/shared/getComponentNameFromType.js @@ -18,6 +18,7 @@ import { REACT_PORTAL_TYPE, REACT_MEMO_TYPE, REACT_PROFILER_TYPE, + REACT_PROVIDER_TYPE, REACT_STRICT_MODE_TYPE, REACT_SUSPENSE_TYPE, REACT_SUSPENSE_LIST_TYPE, @@ -26,7 +27,11 @@ import { REACT_TRACING_MARKER_TYPE, } from 'shared/ReactSymbols'; -import {enableTransitionTracing, enableCache} from './ReactFeatureFlags'; +import { + enableTransitionTracing, + enableCache, + enableRenderableContext, +} from './ReactFeatureFlags'; // Keep in sync with react-reconciler/getComponentNameFromFiber function getWrappedName( @@ -98,12 +103,27 @@ export default function getComponentNameFromType(type: mixed): string | null { } } switch (type.$$typeof) { + case REACT_PROVIDER_TYPE: + if (enableRenderableContext) { + return null; + } else { + const provider = (type: any); + return getContextName(provider._context) + '.Provider'; + } case REACT_CONTEXT_TYPE: const context: ReactContext = (type: any); - return getContextName(context) + '.Provider'; + if (enableRenderableContext) { + return getContextName(context) + '.Provider'; + } else { + return getContextName(context) + '.Consumer'; + } case REACT_CONSUMER_TYPE: - const consumer: ReactConsumerType = (type: any); - return getContextName(consumer._context) + '.Consumer'; + if (enableRenderableContext) { + const consumer: ReactConsumerType = (type: any); + return getContextName(consumer._context) + '.Consumer'; + } else { + return null; + } case REACT_FORWARD_REF_TYPE: return getWrappedName(type, type.render, 'ForwardRef'); case REACT_MEMO_TYPE: diff --git a/packages/shared/isValidElementType.js b/packages/shared/isValidElementType.js index 4529e71f3f393..5dd599ff3ca61 100644 --- a/packages/shared/isValidElementType.js +++ b/packages/shared/isValidElementType.js @@ -10,6 +10,7 @@ import { REACT_CONTEXT_TYPE, REACT_CONSUMER_TYPE, + REACT_PROVIDER_TYPE, REACT_FORWARD_REF_TYPE, REACT_FRAGMENT_TYPE, REACT_PROFILER_TYPE, @@ -31,6 +32,7 @@ import { enableTransitionTracing, enableDebugTracing, enableLegacyHidden, + enableRenderableContext, } from './ReactFeatureFlags'; const REACT_CLIENT_REFERENCE: symbol = Symbol.for('react.client.reference'); @@ -62,6 +64,7 @@ export default function isValidElementType(type: mixed): boolean { type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || + (!enableRenderableContext && type.$$typeof === REACT_PROVIDER_TYPE) || type.$$typeof === REACT_CONSUMER_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object