Skip to content

Commit

Permalink
Add a feature flag for new behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon committed Feb 11, 2024
1 parent 0b14570 commit b122abd
Show file tree
Hide file tree
Showing 26 changed files with 272 additions and 36 deletions.
7 changes: 6 additions & 1 deletion packages/react-client/src/ReactFlightReplyClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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),
Expand Down
6 changes: 5 additions & 1 deletion packages/react-debug-tools/src/ReactDebugHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -821,7 +821,11 @@ function setupContexts(contextMap: Map<ReactContext<any>, any>, fiber: Fiber) {
let current: null | Fiber = fiber;
while (current) {
if (current.tag === ContextProvider) {
const context: ReactContext<any> = current.type;
let context: ReactContext<any> = 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
28 changes: 24 additions & 4 deletions packages/react-is/src/ReactIs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
}
Expand All @@ -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;
Expand All @@ -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 (
Expand Down
27 changes: 22 additions & 5 deletions packages/react-reconciler/src/ReactFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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__) {
Expand Down
30 changes: 25 additions & 5 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ import {
enableFormActions,
enableAsyncActions,
enablePostpone,
enableRenderableContext,
} from 'shared/ReactFeatureFlags';
import isArray from 'shared/isArray';
import shallowEqual from 'shared/shallowEqual';
Expand Down Expand Up @@ -3528,7 +3529,12 @@ function updateContextProvider(
workInProgress: Fiber,
renderLanes: Lanes,
) {
const context: ReactContext<any> = workInProgress.type;
let context: ReactContext<any>;
if (enableRenderableContext) {
context = workInProgress.type;
} else {
context = workInProgress.type._context;
}
const newProps = workInProgress.pendingProps;
const oldProps = workInProgress.memoizedProps;

Expand Down Expand Up @@ -3590,9 +3596,18 @@ function updateContextConsumer(
workInProgress: Fiber,
renderLanes: Lanes,
) {
const consumerType: ReactConsumerType<any> = workInProgress.type;
const context = consumerType._context;

let context: ReactContext<any>;
if (enableRenderableContext) {
const consumerType: ReactConsumerType<any> = 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;

Expand Down Expand Up @@ -3838,7 +3853,12 @@ function attemptEarlyBailoutIfNoScheduledUpdate(
break;
case ContextProvider: {
const newValue = workInProgress.memoizedProps.value;
const context: ReactContext<any> = workInProgress.type;
let context: ReactContext<any>;
if (enableRenderableContext) {
context = workInProgress.type;
} else {
context = workInProgress.type._context;
}
pushProvider(workInProgress, context, newValue);
break;
}
Expand Down
8 changes: 7 additions & 1 deletion packages/react-reconciler/src/ReactFiberCompleteWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
enableCache,
enableTransitionTracing,
enableFloat,
enableRenderableContext,
passChildrenWhenCloningPersistedNodes,
} from 'shared/ReactFeatureFlags';

Expand Down Expand Up @@ -1505,7 +1506,12 @@ function completeWork(
return null;
case ContextProvider:
// Pop provider fiber
const context: ReactContext<any> = workInProgress.type;
let context: ReactContext<any>;
if (enableRenderableContext) {
context = workInProgress.type;
} else {
context = workInProgress.type._context;
}
popProvider(context, workInProgress);
bubbleProperties(workInProgress);
return null;
Expand Down
8 changes: 7 additions & 1 deletion packages/react-reconciler/src/ReactFiberNewContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
enableLazyContextPropagation,
enableFormActions,
enableAsyncActions,
enableRenderableContext,
} from 'shared/ReactFeatureFlags';
import {
getHostTransitionProvider,
Expand Down Expand Up @@ -561,7 +562,12 @@ function propagateParentContextChanges(

const oldProps = currentParent.memoizedProps;
if (oldProps !== null) {
const context: ReactContext<any> = parent.type;
let context: ReactContext<any>;
if (enableRenderableContext) {
context = parent.type;
} else {
context = parent.type._context;
}

const newProps = parent.pendingProps;
const newValue = newProps.value;
Expand Down
10 changes: 8 additions & 2 deletions packages/react-reconciler/src/ReactFiberScope.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -113,7 +116,10 @@ function collectNearestContextValues<T>(
context: ReactContext<T>,
childContextValues: Array<T>,
): 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 {
Expand Down
15 changes: 13 additions & 2 deletions packages/react-reconciler/src/ReactFiberUnwindWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
enableProfilerTimer,
enableCache,
enableTransitionTracing,
enableRenderableContext,
} from 'shared/ReactFeatureFlags';

import {popHostContainer, popHostContext} from './ReactFiberHostContext';
Expand Down Expand Up @@ -160,7 +161,12 @@ function unwindWork(
popHostContainer(workInProgress);
return null;
case ContextProvider:
const context: ReactContext<any> = workInProgress.type;
let context: ReactContext<any>;
if (enableRenderableContext) {
context = workInProgress.type;
} else {
context = workInProgress.type._context;
}
popProvider(context, workInProgress);
return null;
case OffscreenComponent:
Expand Down Expand Up @@ -250,7 +256,12 @@ function unwindInterruptedWork(
popSuspenseListContext(interruptedWork);
break;
case ContextProvider:
const context: ReactContext<any> = interruptedWork.type;
let context: ReactContext<any>;
if (enableRenderableContext) {
context = interruptedWork.type;
} else {
context = interruptedWork.type._context;
}
popProvider(context, interruptedWork);
break;
case OffscreenComponent:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,7 @@ describe('ReactNewContext', () => {
);
});

// @gate enableRenderableContext
it('warns when passed a consumer', async () => {
const Context = React.createContext(0);
function Foo() {
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit b122abd

Please sign in to comment.