mirror of
https://github.com/facebook/react.git
synced 2026-02-25 23:45:10 +00:00
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 5696782b428a5ace96e66c1857e13249b6c07958.** 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.
149 lines
4.3 KiB
JavaScript
149 lines
4.3 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
import type {LazyComponent} from 'react/src/ReactLazy';
|
|
import type {ReactContext, ReactConsumerType} from 'shared/ReactTypes';
|
|
|
|
import {
|
|
REACT_CONTEXT_TYPE,
|
|
REACT_CONSUMER_TYPE,
|
|
REACT_FORWARD_REF_TYPE,
|
|
REACT_FRAGMENT_TYPE,
|
|
REACT_PORTAL_TYPE,
|
|
REACT_MEMO_TYPE,
|
|
REACT_PROFILER_TYPE,
|
|
REACT_PROVIDER_TYPE,
|
|
REACT_STRICT_MODE_TYPE,
|
|
REACT_SUSPENSE_TYPE,
|
|
REACT_SUSPENSE_LIST_TYPE,
|
|
REACT_LAZY_TYPE,
|
|
REACT_CACHE_TYPE,
|
|
REACT_TRACING_MARKER_TYPE,
|
|
} from 'shared/ReactSymbols';
|
|
|
|
import {
|
|
enableTransitionTracing,
|
|
enableCache,
|
|
enableRenderableContext,
|
|
} from './ReactFeatureFlags';
|
|
|
|
// Keep in sync with react-reconciler/getComponentNameFromFiber
|
|
function getWrappedName(
|
|
outerType: mixed,
|
|
innerType: any,
|
|
wrapperName: string,
|
|
): string {
|
|
const displayName = (outerType: any).displayName;
|
|
if (displayName) {
|
|
return displayName;
|
|
}
|
|
const functionName = innerType.displayName || innerType.name || '';
|
|
return functionName !== '' ? `${wrapperName}(${functionName})` : wrapperName;
|
|
}
|
|
|
|
// Keep in sync with react-reconciler/getComponentNameFromFiber
|
|
function getContextName(type: ReactContext<any>) {
|
|
return type.displayName || 'Context';
|
|
}
|
|
|
|
const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference');
|
|
|
|
// Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead.
|
|
export default function getComponentNameFromType(type: mixed): string | null {
|
|
if (type == null) {
|
|
// Host root, text node or just invalid type.
|
|
return null;
|
|
}
|
|
if (typeof type === 'function') {
|
|
if ((type: any).$$typeof === REACT_CLIENT_REFERENCE) {
|
|
// TODO: Create a convention for naming client references with debug info.
|
|
return null;
|
|
}
|
|
return (type: any).displayName || type.name || null;
|
|
}
|
|
if (typeof type === 'string') {
|
|
return type;
|
|
}
|
|
switch (type) {
|
|
case REACT_FRAGMENT_TYPE:
|
|
return 'Fragment';
|
|
case REACT_PORTAL_TYPE:
|
|
return 'Portal';
|
|
case REACT_PROFILER_TYPE:
|
|
return 'Profiler';
|
|
case REACT_STRICT_MODE_TYPE:
|
|
return 'StrictMode';
|
|
case REACT_SUSPENSE_TYPE:
|
|
return 'Suspense';
|
|
case REACT_SUSPENSE_LIST_TYPE:
|
|
return 'SuspenseList';
|
|
case REACT_CACHE_TYPE:
|
|
if (enableCache) {
|
|
return 'Cache';
|
|
}
|
|
// Fall through
|
|
case REACT_TRACING_MARKER_TYPE:
|
|
if (enableTransitionTracing) {
|
|
return 'TracingMarker';
|
|
}
|
|
}
|
|
if (typeof type === 'object') {
|
|
if (__DEV__) {
|
|
if (typeof (type: any).tag === 'number') {
|
|
console.error(
|
|
'Received an unexpected object in getComponentNameFromType(). ' +
|
|
'This is likely a bug in React. Please file an issue.',
|
|
);
|
|
}
|
|
}
|
|
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<any> = (type: any);
|
|
if (enableRenderableContext) {
|
|
return getContextName(context) + '.Provider';
|
|
} else {
|
|
return getContextName(context) + '.Consumer';
|
|
}
|
|
case REACT_CONSUMER_TYPE:
|
|
if (enableRenderableContext) {
|
|
const consumer: ReactConsumerType<any> = (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:
|
|
const outerName = (type: any).displayName || null;
|
|
if (outerName !== null) {
|
|
return outerName;
|
|
}
|
|
return getComponentNameFromType(type.type) || 'Memo';
|
|
case REACT_LAZY_TYPE: {
|
|
const lazyComponent: LazyComponent<any, any> = (type: any);
|
|
const payload = lazyComponent._payload;
|
|
const init = lazyComponent._init;
|
|
try {
|
|
return getComponentNameFromType(init(payload));
|
|
} catch (x) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|