Skip to content

Commit 1985f4f

Browse files
committed
Switch <Context> to mean <Context.Provider> (#28226)
Previously, `<Context>` was equivalent to `<Context.Consumer>`. However, since the introduction of Hooks, the `<Context.Consumer>` API is rarely used. The goal here is to make the common case cleaner: ```js const ThemeContext = createContext('light') function App() { return ( <ThemeContext value="dark"> ... </ThemeContext> ) } function Button() { const theme = use(ThemeContext) // ... } ``` This is technically a breaking change, but we've been warning about rendering `<Context>` directly for several years by now, so it's unlikely much code in the wild depends on the old behavior. [Proof that it warns today (check console).](https://codesandbox.io/p/sandbox/peaceful-nobel-pdxtfl) --- **The relevant commit is 5696782.** It switches `createContext` implementation so that `Context.Provider === Context`. The main assumption that changed is that a Provider's fiber type is now the context itself (rather than an intermediate object). Whereas a Consumer's fiber type is now always an intermediate object (rather than it being sometimes the context itself and sometimes an intermediate object). My methodology was to start with the relevant symbols, work tags, and types, and work my way backwards to all usages. This might break tooling that depends on inspecting React's internal fields. I've added DevTools support in the second commit. This didn't need explicit versioning—the structure tells us enough. DiffTrain build for [14fd963](14fd963)
1 parent 69dc39f commit 1985f4f

37 files changed

+4244
-3219
lines changed

compiled/facebook-www/JSXDEVRuntime-dev.classic.js

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ if (__DEV__) {
2727
var REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
2828
var REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode");
2929
var REACT_PROFILER_TYPE = Symbol.for("react.profiler");
30-
var REACT_PROVIDER_TYPE = Symbol.for("react.provider");
30+
var REACT_PROVIDER_TYPE = Symbol.for("react.provider"); // TODO: Delete with enableRenderableContext
31+
32+
var REACT_CONSUMER_TYPE = Symbol.for("react.consumer");
3133
var REACT_CONTEXT_TYPE = Symbol.for("react.context");
3234
var REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref");
3335
var REACT_SUSPENSE_TYPE = Symbol.for("react.suspense");
@@ -106,8 +108,8 @@ if (__DEV__) {
106108
var dynamicFeatureFlags = require("ReactFeatureFlags");
107109

108110
var enableDebugTracing = dynamicFeatureFlags.enableDebugTracing,
109-
enableTransitionTracing = dynamicFeatureFlags.enableTransitionTracing;
110-
// On WWW, false is used for a new modern build.
111+
enableTransitionTracing = dynamicFeatureFlags.enableTransitionTracing,
112+
enableRenderableContext = dynamicFeatureFlags.enableRenderableContext; // On WWW, false is used for a new modern build.
111113

112114
var REACT_CLIENT_REFERENCE$2 = Symbol.for("react.client.reference");
113115
function isValidElementType(type) {
@@ -135,8 +137,9 @@ if (__DEV__) {
135137
if (
136138
type.$$typeof === REACT_LAZY_TYPE ||
137139
type.$$typeof === REACT_MEMO_TYPE ||
138-
type.$$typeof === REACT_PROVIDER_TYPE ||
139140
type.$$typeof === REACT_CONTEXT_TYPE ||
141+
(!enableRenderableContext && type.$$typeof === REACT_PROVIDER_TYPE) ||
142+
(enableRenderableContext && type.$$typeof === REACT_CONSUMER_TYPE) ||
140143
type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object
141144
// types supported by any Flight configuration anywhere since
142145
// we don't know which Flight build this will end up being used
@@ -231,13 +234,30 @@ if (__DEV__) {
231234
}
232235

233236
switch (type.$$typeof) {
237+
case REACT_PROVIDER_TYPE:
238+
if (enableRenderableContext) {
239+
return null;
240+
} else {
241+
var provider = type;
242+
return getContextName(provider._context) + ".Provider";
243+
}
244+
234245
case REACT_CONTEXT_TYPE:
235246
var context = type;
236-
return getContextName(context) + ".Consumer";
237247

238-
case REACT_PROVIDER_TYPE:
239-
var provider = type;
240-
return getContextName(provider._context) + ".Provider";
248+
if (enableRenderableContext) {
249+
return getContextName(context) + ".Provider";
250+
} else {
251+
return getContextName(context) + ".Consumer";
252+
}
253+
254+
case REACT_CONSUMER_TYPE:
255+
if (enableRenderableContext) {
256+
var consumer = type;
257+
return getContextName(consumer._context) + ".Consumer";
258+
} else {
259+
return null;
260+
}
241261

242262
case REACT_FORWARD_REF_TYPE:
243263
return getWrappedName(type, type.render, "ForwardRef");

compiled/facebook-www/JSXDEVRuntime-dev.modern.js

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ if (__DEV__) {
2727
var REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
2828
var REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode");
2929
var REACT_PROFILER_TYPE = Symbol.for("react.profiler");
30-
var REACT_PROVIDER_TYPE = Symbol.for("react.provider");
30+
var REACT_PROVIDER_TYPE = Symbol.for("react.provider"); // TODO: Delete with enableRenderableContext
31+
32+
var REACT_CONSUMER_TYPE = Symbol.for("react.consumer");
3133
var REACT_CONTEXT_TYPE = Symbol.for("react.context");
3234
var REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref");
3335
var REACT_SUSPENSE_TYPE = Symbol.for("react.suspense");
@@ -106,8 +108,8 @@ if (__DEV__) {
106108
var dynamicFeatureFlags = require("ReactFeatureFlags");
107109

108110
var enableDebugTracing = dynamicFeatureFlags.enableDebugTracing,
109-
enableTransitionTracing = dynamicFeatureFlags.enableTransitionTracing;
110-
// On WWW, true is used for a new modern build.
111+
enableTransitionTracing = dynamicFeatureFlags.enableTransitionTracing,
112+
enableRenderableContext = dynamicFeatureFlags.enableRenderableContext; // On WWW, true is used for a new modern build.
111113

112114
var REACT_CLIENT_REFERENCE$2 = Symbol.for("react.client.reference");
113115
function isValidElementType(type) {
@@ -135,8 +137,9 @@ if (__DEV__) {
135137
if (
136138
type.$$typeof === REACT_LAZY_TYPE ||
137139
type.$$typeof === REACT_MEMO_TYPE ||
138-
type.$$typeof === REACT_PROVIDER_TYPE ||
139140
type.$$typeof === REACT_CONTEXT_TYPE ||
141+
(!enableRenderableContext && type.$$typeof === REACT_PROVIDER_TYPE) ||
142+
(enableRenderableContext && type.$$typeof === REACT_CONSUMER_TYPE) ||
140143
type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object
141144
// types supported by any Flight configuration anywhere since
142145
// we don't know which Flight build this will end up being used
@@ -231,13 +234,30 @@ if (__DEV__) {
231234
}
232235

233236
switch (type.$$typeof) {
237+
case REACT_PROVIDER_TYPE:
238+
if (enableRenderableContext) {
239+
return null;
240+
} else {
241+
var provider = type;
242+
return getContextName(provider._context) + ".Provider";
243+
}
244+
234245
case REACT_CONTEXT_TYPE:
235246
var context = type;
236-
return getContextName(context) + ".Consumer";
237247

238-
case REACT_PROVIDER_TYPE:
239-
var provider = type;
240-
return getContextName(provider._context) + ".Provider";
248+
if (enableRenderableContext) {
249+
return getContextName(context) + ".Provider";
250+
} else {
251+
return getContextName(context) + ".Consumer";
252+
}
253+
254+
case REACT_CONSUMER_TYPE:
255+
if (enableRenderableContext) {
256+
var consumer = type;
257+
return getContextName(consumer._context) + ".Consumer";
258+
} else {
259+
return null;
260+
}
241261

242262
case REACT_FORWARD_REF_TYPE:
243263
return getWrappedName(type, type.render, "ForwardRef");

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8d48183291870898ec42ac1f84482d9d26789424
1+
14fd9630ee04387f4361da289393234e2b7d93b6

compiled/facebook-www/React-dev.classic.js

Lines changed: 88 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ if (__DEV__) {
2424
) {
2525
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
2626
}
27-
var ReactVersion = "18.3.0-www-classic-ab1e0612";
27+
var ReactVersion = "18.3.0-www-classic-38e30556";
2828

2929
// ATTENTION
3030
// When adding new symbols to this file,
@@ -35,7 +35,9 @@ if (__DEV__) {
3535
var REACT_FRAGMENT_TYPE = Symbol.for("react.fragment");
3636
var REACT_STRICT_MODE_TYPE = Symbol.for("react.strict_mode");
3737
var REACT_PROFILER_TYPE = Symbol.for("react.profiler");
38-
var REACT_PROVIDER_TYPE = Symbol.for("react.provider");
38+
var REACT_PROVIDER_TYPE = Symbol.for("react.provider"); // TODO: Delete with enableRenderableContext
39+
40+
var REACT_CONSUMER_TYPE = Symbol.for("react.consumer");
3941
var REACT_CONTEXT_TYPE = Symbol.for("react.context");
4042
var REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref");
4143
var REACT_SUSPENSE_TYPE = Symbol.for("react.suspense");
@@ -473,8 +475,8 @@ if (__DEV__) {
473475

474476
var enableDebugTracing = dynamicFeatureFlags.enableDebugTracing,
475477
enableTransitionTracing = dynamicFeatureFlags.enableTransitionTracing,
476-
enableAsyncActions = dynamicFeatureFlags.enableAsyncActions;
477-
// On WWW, false is used for a new modern build.
478+
enableAsyncActions = dynamicFeatureFlags.enableAsyncActions,
479+
enableRenderableContext = dynamicFeatureFlags.enableRenderableContext; // On WWW, false is used for a new modern build.
478480

479481
function getWrappedName(outerType, innerType, wrapperName) {
480482
var displayName = outerType.displayName;
@@ -556,13 +558,30 @@ if (__DEV__) {
556558
}
557559

558560
switch (type.$$typeof) {
561+
case REACT_PROVIDER_TYPE:
562+
if (enableRenderableContext) {
563+
return null;
564+
} else {
565+
var provider = type;
566+
return getContextName(provider._context) + ".Provider";
567+
}
568+
559569
case REACT_CONTEXT_TYPE:
560570
var context = type;
561-
return getContextName(context) + ".Consumer";
562571

563-
case REACT_PROVIDER_TYPE:
564-
var provider = type;
565-
return getContextName(provider._context) + ".Provider";
572+
if (enableRenderableContext) {
573+
return getContextName(context) + ".Provider";
574+
} else {
575+
return getContextName(context) + ".Consumer";
576+
}
577+
578+
case REACT_CONSUMER_TYPE:
579+
if (enableRenderableContext) {
580+
var consumer = type;
581+
return getContextName(consumer._context) + ".Consumer";
582+
} else {
583+
return null;
584+
}
566585

567586
case REACT_FORWARD_REF_TYPE:
568587
return getWrappedName(type, type.render, "ForwardRef");
@@ -1022,8 +1041,9 @@ if (__DEV__) {
10221041
if (
10231042
type.$$typeof === REACT_LAZY_TYPE ||
10241043
type.$$typeof === REACT_MEMO_TYPE ||
1025-
type.$$typeof === REACT_PROVIDER_TYPE ||
10261044
type.$$typeof === REACT_CONTEXT_TYPE ||
1045+
(!enableRenderableContext && type.$$typeof === REACT_PROVIDER_TYPE) ||
1046+
(enableRenderableContext && type.$$typeof === REACT_CONSUMER_TYPE) ||
10271047
type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object
10281048
// types supported by any Flight configuration anywhere since
10291049
// we don't know which Flight build this will end up being used
@@ -2472,98 +2492,71 @@ if (__DEV__) {
24722492
Provider: null,
24732493
Consumer: null
24742494
};
2475-
context.Provider = {
2476-
$$typeof: REACT_PROVIDER_TYPE,
2477-
_context: context
2478-
};
2479-
var hasWarnedAboutUsingNestedContextConsumers = false;
2480-
var hasWarnedAboutUsingConsumerProvider = false;
2481-
var hasWarnedAboutDisplayNameOnConsumer = false;
24822495

2483-
{
2484-
// A separate object, but proxies back to the original context object for
2485-
// backwards compatibility. It has a different $$typeof, so we can properly
2486-
// warn for the incorrect usage of Context as a Consumer.
2487-
var Consumer = {
2488-
$$typeof: REACT_CONTEXT_TYPE,
2496+
if (enableRenderableContext) {
2497+
context.Provider = context;
2498+
context.Consumer = {
2499+
$$typeof: REACT_CONSUMER_TYPE,
24892500
_context: context
2490-
}; // $FlowFixMe[prop-missing]: Flow complains about not setting a value, which is intentional here
2491-
2492-
Object.defineProperties(Consumer, {
2493-
Provider: {
2494-
get: function () {
2495-
if (!hasWarnedAboutUsingConsumerProvider) {
2496-
hasWarnedAboutUsingConsumerProvider = true;
2501+
};
2502+
} else {
2503+
context.Provider = {
2504+
$$typeof: REACT_PROVIDER_TYPE,
2505+
_context: context
2506+
};
24972507

2498-
error(
2499-
"Rendering <Context.Consumer.Provider> is not supported and will be removed in " +
2500-
"a future major release. Did you mean to render <Context.Provider> instead?"
2501-
);
2508+
{
2509+
var Consumer = {
2510+
$$typeof: REACT_CONTEXT_TYPE,
2511+
_context: context
2512+
};
2513+
Object.defineProperties(Consumer, {
2514+
Provider: {
2515+
get: function () {
2516+
return context.Provider;
2517+
},
2518+
set: function (_Provider) {
2519+
context.Provider = _Provider;
25022520
}
2503-
2504-
return context.Provider;
25052521
},
2506-
set: function (_Provider) {
2507-
context.Provider = _Provider;
2508-
}
2509-
},
2510-
_currentValue: {
2511-
get: function () {
2512-
return context._currentValue;
2513-
},
2514-
set: function (_currentValue) {
2515-
context._currentValue = _currentValue;
2516-
}
2517-
},
2518-
_currentValue2: {
2519-
get: function () {
2520-
return context._currentValue2;
2522+
_currentValue: {
2523+
get: function () {
2524+
return context._currentValue;
2525+
},
2526+
set: function (_currentValue) {
2527+
context._currentValue = _currentValue;
2528+
}
25212529
},
2522-
set: function (_currentValue2) {
2523-
context._currentValue2 = _currentValue2;
2524-
}
2525-
},
2526-
_threadCount: {
2527-
get: function () {
2528-
return context._threadCount;
2530+
_currentValue2: {
2531+
get: function () {
2532+
return context._currentValue2;
2533+
},
2534+
set: function (_currentValue2) {
2535+
context._currentValue2 = _currentValue2;
2536+
}
25292537
},
2530-
set: function (_threadCount) {
2531-
context._threadCount = _threadCount;
2532-
}
2533-
},
2534-
Consumer: {
2535-
get: function () {
2536-
if (!hasWarnedAboutUsingNestedContextConsumers) {
2537-
hasWarnedAboutUsingNestedContextConsumers = true;
2538-
2539-
error(
2540-
"Rendering <Context.Consumer.Consumer> is not supported and will be removed in " +
2541-
"a future major release. Did you mean to render <Context.Consumer> instead?"
2542-
);
2538+
_threadCount: {
2539+
get: function () {
2540+
return context._threadCount;
2541+
},
2542+
set: function (_threadCount) {
2543+
context._threadCount = _threadCount;
25432544
}
2544-
2545-
return context.Consumer;
2546-
}
2547-
},
2548-
displayName: {
2549-
get: function () {
2550-
return context.displayName;
25512545
},
2552-
set: function (displayName) {
2553-
if (!hasWarnedAboutDisplayNameOnConsumer) {
2554-
warn(
2555-
"Setting `displayName` on Context.Consumer has no effect. " +
2556-
"You should set it directly on the context with Context.displayName = '%s'.",
2557-
displayName
2558-
);
2559-
2560-
hasWarnedAboutDisplayNameOnConsumer = true;
2546+
Consumer: {
2547+
get: function () {
2548+
return context.Consumer;
25612549
}
2550+
},
2551+
displayName: {
2552+
get: function () {
2553+
return context.displayName;
2554+
},
2555+
set: function (displayName) {}
25622556
}
2563-
}
2564-
}); // $FlowFixMe[prop-missing]: Flow complains about missing properties because it doesn't understand defineProperty
2565-
2566-
context.Consumer = Consumer;
2557+
});
2558+
context.Consumer = Consumer;
2559+
}
25672560
}
25682561

25692562
{
@@ -2910,22 +2903,11 @@ if (__DEV__) {
29102903
var dispatcher = resolveDispatcher();
29112904

29122905
{
2913-
// TODO: add a more generic warning for invalid values.
2914-
if (Context._context !== undefined) {
2915-
var realContext = Context._context; // Don't deduplicate because this legitimately causes bugs
2916-
// and nobody should be using this in existing code.
2917-
2918-
if (realContext.Consumer === Context) {
2919-
error(
2920-
"Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be " +
2921-
"removed in a future major release. Did you mean to call useContext(Context) instead?"
2922-
);
2923-
} else if (realContext.Provider === Context) {
2924-
error(
2925-
"Calling useContext(Context.Provider) is not supported. " +
2926-
"Did you mean to call useContext(Context) instead?"
2927-
);
2928-
}
2906+
if (Context.$$typeof === REACT_CONSUMER_TYPE) {
2907+
error(
2908+
"Calling useContext(Context.Consumer) is not supported and will cause bugs. " +
2909+
"Did you mean to call useContext(Context) instead?"
2910+
);
29292911
}
29302912
}
29312913

0 commit comments

Comments
 (0)