@samitouri / QOS-React / commits / 94eed63c49

(Land #28798) Move Current Owner (and Cache) to an Async Dispatcher (#28912)

Rebasing and landing https://github.com/facebook/react/pull/28798 This PR was approved already but held back to give time for the sync. Rebased and landing here without pushing to seb's remote to avoid possibility of lost updates --------- Co-authored-by: Sebastian Markbage <sebastian@calyptus.eu>

Josh Story committed Apr 25, 2024 at 10:40 UTC 94eed63c49d989861ae7cd62e111de6d717f0a10
21 files changed +231 -176
packages/react-dom/src/__tests__/ReactCompositeComponent-test.js
+21 -6
@@ -537,16 +537,24 @@ describe('ReactCompositeComponent', () => {
537 });
538
539 it('should cleanup even if render() fatals', async () => {
540 + const dispatcherEnabled =
541 + __DEV__ ||
542 + !gate(flags => flags.disableStringRefs) ||
543 + gate(flags => flags.enableCache);
544 + const ownerEnabled = __DEV__ || !gate(flags => flags.disableStringRefs);
545 +
546 + let stashedDispatcher;
547 class BadComponent extends React.Component {
548 render() {
549 + // Stash the dispatcher that was available in render so we can check
550 + // that its internals also reset.
551 + stashedDispatcher = ReactSharedInternals.A;
552 throw new Error();
553 }
554 }
555
556 const instance = <BadComponent />;
547 - expect(ReactSharedInternals.owner).toBe(
548 - __DEV__ || !gate(flags => flags.disableStringRefs) ? null : undefined,
549 - );
557 + expect(ReactSharedInternals.A).toBe(dispatcherEnabled ? null : undefined);
558
559 const root = ReactDOMClient.createRoot(document.createElement('div'));
560 await expect(async () => {
@@ -555,9 +563,16 @@ describe('ReactCompositeComponent', () => {
563 });
564 }).rejects.toThrow();
565
558 - expect(ReactSharedInternals.owner).toBe(
559 - __DEV__ || !gate(flags => flags.disableStringRefs) ? null : undefined,
560 - );
566 + expect(ReactSharedInternals.A).toBe(dispatcherEnabled ? null : undefined);
567 + if (dispatcherEnabled) {
568 + if (ownerEnabled) {
569 + expect(stashedDispatcher.getOwner()).toBe(null);
570 + } else {
571 + expect(stashedDispatcher.getOwner).toBe(undefined);
572 + }
573 + } else {
574 + expect(stashedDispatcher).toBe(undefined);
575 + }
576 });
577
578 it('should call componentWillUnmount before unmounting', async () => {
packages/react-dom/src/client/ReactDOMRootFB.js
+2 -2
@@ -61,7 +61,7 @@ import {LegacyRoot} from 'react-reconciler/src/ReactRootTags';
61 import getComponentNameFromType from 'shared/getComponentNameFromType';
62 import {has as hasInstance} from 'shared/ReactInstanceMap';
63
64 -import ReactSharedInternals from 'shared/ReactSharedInternals';
64 +import {currentOwner} from 'react-reconciler/src/ReactFiberCurrentOwner';
65
66 import assign from 'shared/assign';
67
@@ -342,7 +342,7 @@ export function findDOMNode(
342 componentOrElement: Element | ?React$Component<any, any>,
343 ): null | Element | Text {
344 if (__DEV__) {
345 - const owner = (ReactSharedInternals.owner: any);
345 + const owner = currentOwner;
346 if (owner !== null && owner.stateNode !== null) {
347 const warnedAboutRefsInRender = owner.stateNode._warnedAboutRefsInRender;
348 if (!warnedAboutRefsInRender) {
packages/react-native-renderer/src/ReactNativePublicCompat.js
+3 -3
@@ -24,14 +24,14 @@ import {
24 findHostInstanceWithWarning,
25 } from 'react-reconciler/src/ReactFiberReconciler';
26 import {doesFiberContain} from 'react-reconciler/src/ReactFiberTreeReflection';
27 -import ReactSharedInternals from 'shared/ReactSharedInternals';
27 import getComponentNameFromType from 'shared/getComponentNameFromType';
28 +import {currentOwner} from 'react-reconciler/src/ReactFiberCurrentOwner';
29
30 export function findHostInstance_DEPRECATED<TElementType: ElementType>(
31 componentOrHandle: ?(ElementRef<TElementType> | number),
32 ): ?ElementRef<HostComponent<mixed>> {
33 if (__DEV__) {
34 - const owner = ReactSharedInternals.owner;
34 + const owner = currentOwner;
35 if (owner !== null && owner.stateNode !== null) {
36 if (!owner.stateNode._warnedAboutRefsInRender) {
37 console.error(
@@ -86,7 +86,7 @@ export function findHostInstance_DEPRECATED<TElementType: ElementType>(
86
87 export function findNodeHandle(componentOrHandle: any): ?number {
88 if (__DEV__) {
89 - const owner = ReactSharedInternals.owner;
89 + const owner = currentOwner;
90 if (owner !== null && owner.stateNode !== null) {
91 if (!owner.stateNode._warnedAboutRefsInRender) {
92 console.error(
packages/react-reconciler/src/ReactFiberAsyncDispatcher.js renamed
+13 -3
@@ -7,13 +7,17 @@
7 * @flow
8 */
9
10 -import type {CacheDispatcher} from './ReactInternalTypes';
10 +import type {AsyncDispatcher, Fiber} from './ReactInternalTypes';
11 import type {Cache} from './ReactFiberCacheComponent';
12
13 import {enableCache} from 'shared/ReactFeatureFlags';
14 import {readContext} from './ReactFiberNewContext';
15 import {CacheContext} from './ReactFiberCacheComponent';
16
17 +import {disableStringRefs} from 'shared/ReactFeatureFlags';
18 +
19 +import {currentOwner} from './ReactFiberCurrentOwner';
20 +
21 function getCacheForType<T>(resourceType: () => T): T {
22 if (!enableCache) {
23 throw new Error('Not implemented.');
@@ -27,6 +31,12 @@ function getCacheForType<T>(resourceType: () => T): T {
31 return cacheForType;
32 }
33
30 -export const DefaultCacheDispatcher: CacheDispatcher = {
34 +export const DefaultAsyncDispatcher: AsyncDispatcher = ({
35 getCacheForType,
32 -};
36 +}: any);
37 +
38 +if (__DEV__ || !disableStringRefs) {
39 + DefaultAsyncDispatcher.getOwner = (): null | Fiber => {
40 + return currentOwner;
41 + };
42 +}
packages/react-reconciler/src/ReactFiberBeginWork.js
+5 -5
@@ -91,7 +91,6 @@ import {
91 Passive,
92 DidDefer,
93 } from './ReactFiberFlags';
94 -import ReactSharedInternals from 'shared/ReactSharedInternals';
94 import {
95 debugRenderPhaseSideEffectsForStrictMode,
96 disableLegacyContext,
@@ -297,6 +296,7 @@ import {
296 pushRootMarkerInstance,
297 TransitionTracingMarker,
298 } from './ReactFiberTracingMarkerComponent';
299 +import {setCurrentOwner} from './ReactFiberCurrentOwner';
300
301 // A special exception that's used to unwind the stack when an update flows
302 // into a dehydrated boundary.
@@ -432,7 +432,7 @@ function updateForwardRef(
432 markComponentRenderStarted(workInProgress);
433 }
434 if (__DEV__) {
435 - ReactSharedInternals.owner = workInProgress;
435 + setCurrentOwner(workInProgress);
436 setIsRendering(true);
437 nextChildren = renderWithHooks(
438 current,
@@ -1150,7 +1150,7 @@ function updateFunctionComponent(
1150 markComponentRenderStarted(workInProgress);
1151 }
1152 if (__DEV__) {
1153 - ReactSharedInternals.owner = workInProgress;
1153 + setCurrentOwner(workInProgress);
1154 setIsRendering(true);
1155 nextChildren = renderWithHooks(
1156 current,
@@ -1373,7 +1373,7 @@ function finishClassComponent(
1373
1374 // Rerender
1375 if (__DEV__ || !disableStringRefs) {
1376 - ReactSharedInternals.owner = workInProgress;
1376 + setCurrentOwner(workInProgress);
1377 }
1378 let nextChildren;
1379 if (
@@ -3419,7 +3419,7 @@ function updateContextConsumer(
3419 }
3420 let newChildren;
3421 if (__DEV__) {
3422 - ReactSharedInternals.owner = workInProgress;
3422 + setCurrentOwner(workInProgress);
3423 setIsRendering(true);
3424 newChildren = render(newValue);
3425 setIsRendering(false);
packages/react-reconciler/src/ReactFiberCurrentOwner.js new
+16
@@ -0,0 +1,16 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {Fiber} from './ReactInternalTypes';
11 +
12 +export let currentOwner: Fiber | null = null;
13 +
14 +export function setCurrentOwner(fiber: null | Fiber) {
15 + currentOwner = fiber;
16 +}
packages/react-reconciler/src/ReactFiberTreeReflection.js
+2 -2
@@ -12,7 +12,6 @@ import type {Container, SuspenseInstance} from './ReactFiberConfig';
12 import type {SuspenseState} from './ReactFiberSuspenseComponent';
13
14 import {get as getInstance} from 'shared/ReactInstanceMap';
15 -import ReactSharedInternals from 'shared/ReactSharedInternals';
15 import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
16 import {
17 ClassComponent,
@@ -25,6 +24,7 @@ import {
24 SuspenseComponent,
25 } from './ReactWorkTags';
26 import {NoFlags, Placement, Hydrating} from './ReactFiberFlags';
27 +import {currentOwner} from './ReactFiberCurrentOwner';
28
29 export function getNearestMountedFiber(fiber: Fiber): null | Fiber {
30 let node = fiber;
@@ -89,7 +89,7 @@ export function isFiberMounted(fiber: Fiber): boolean {
89
90 export function isMounted(component: React$Component<any, any>): boolean {
91 if (__DEV__) {
92 - const owner = (ReactSharedInternals.owner: any);
92 + const owner = currentOwner;
93 if (owner !== null && owner.tag === ClassComponent) {
94 const ownerFiber: Fiber = owner;
95 const instance = ownerFiber.stateNode;
packages/react-reconciler/src/ReactFiberWorkLoop.js
+18 -17
@@ -203,7 +203,8 @@ import {
203 resetHooksOnUnwind,
204 ContextOnlyDispatcher,
205 } from './ReactFiberHooks';
206 -import {DefaultCacheDispatcher} from './ReactFiberCache';
206 +import {DefaultAsyncDispatcher} from './ReactFiberAsyncDispatcher';
207 +import {setCurrentOwner} from './ReactFiberCurrentOwner';
208 import {
209 createCapturedValueAtFiber,
210 type CapturedValue,
@@ -1684,7 +1685,7 @@ function handleThrow(root: FiberRoot, thrownValue: any): void {
1685 resetHooksAfterThrow();
1686 resetCurrentDebugFiberInDEV();
1687 if (__DEV__ || !disableStringRefs) {
1687 - ReactSharedInternals.owner = null;
1688 + setCurrentOwner(null);
1689 }
1690
1691 if (thrownValue === SuspenseException) {
@@ -1874,19 +1875,19 @@ function popDispatcher(prevDispatcher: any) {
1875 ReactSharedInternals.H = prevDispatcher;
1876 }
1877
1877 -function pushCacheDispatcher() {
1878 - if (enableCache) {
1879 - const prevCacheDispatcher = ReactSharedInternals.C;
1880 - ReactSharedInternals.C = DefaultCacheDispatcher;
1881 - return prevCacheDispatcher;
1878 +function pushAsyncDispatcher() {
1879 + if (enableCache || __DEV__ || !disableStringRefs) {
1880 + const prevAsyncDispatcher = ReactSharedInternals.A;
1881 + ReactSharedInternals.A = DefaultAsyncDispatcher;
1882 + return prevAsyncDispatcher;
1883 } else {
1884 return null;
1885 }
1886 }
1887
1887 -function popCacheDispatcher(prevCacheDispatcher: any) {
1888 - if (enableCache) {
1889 - ReactSharedInternals.C = prevCacheDispatcher;
1888 +function popAsyncDispatcher(prevAsyncDispatcher: any) {
1889 + if (enableCache || __DEV__ || !disableStringRefs) {
1890 + ReactSharedInternals.A = prevAsyncDispatcher;
1891 }
1892 }
1893
@@ -1963,7 +1964,7 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
1964 const prevExecutionContext = executionContext;
1965 executionContext |= RenderContext;
1966 const prevDispatcher = pushDispatcher(root.containerInfo);
1966 - const prevCacheDispatcher = pushCacheDispatcher();
1967 + const prevAsyncDispatcher = pushAsyncDispatcher();
1968
1969 // If the root or lanes have changed, throw out the existing stack
1970 // and prepare a fresh one. Otherwise we'll continue where we left off.
@@ -2061,7 +2062,7 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
2062
2063 executionContext = prevExecutionContext;
2064 popDispatcher(prevDispatcher);
2064 - popCacheDispatcher(prevCacheDispatcher);
2065 + popAsyncDispatcher(prevAsyncDispatcher);
2066
2067 if (workInProgress !== null) {
2068 // This is a sync render, so we should have finished the whole tree.
@@ -2104,7 +2105,7 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
2105 const prevExecutionContext = executionContext;
2106 executionContext |= RenderContext;
2107 const prevDispatcher = pushDispatcher(root.containerInfo);
2107 - const prevCacheDispatcher = pushCacheDispatcher();
2108 + const prevAsyncDispatcher = pushAsyncDispatcher();
2109
2110 // If the root or lanes have changed, throw out the existing stack
2111 // and prepare a fresh one. Otherwise we'll continue where we left off.
@@ -2317,7 +2318,7 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
2318 resetContextDependencies();
2319
2320 popDispatcher(prevDispatcher);
2320 - popCacheDispatcher(prevCacheDispatcher);
2321 + popAsyncDispatcher(prevAsyncDispatcher);
2322 executionContext = prevExecutionContext;
2323
2324 if (__DEV__) {
@@ -2386,7 +2387,7 @@ function performUnitOfWork(unitOfWork: Fiber): void {
2387 }
2388
2389 if (__DEV__ || !disableStringRefs) {
2389 - ReactSharedInternals.owner = null;
2390 + setCurrentOwner(null);
2391 }
2392 }
2393
@@ -2501,7 +2502,7 @@ function replaySuspendedUnitOfWork(unitOfWork: Fiber): void {
2502 }
2503
2504 if (__DEV__ || !disableStringRefs) {
2504 - ReactSharedInternals.owner = null;
2505 + setCurrentOwner(null);
2506 }
2507 }
2508
@@ -2894,7 +2895,7 @@ function commitRootImpl(
2895
2896 // Reset this to null before calling lifecycles
2897 if (__DEV__ || !disableStringRefs) {
2897 - ReactSharedInternals.owner = null;
2898 + setCurrentOwner(null);
2899 }
2900
2901 // The commit phase is broken into several sub-phases. We do a separate pass
packages/react-reconciler/src/ReactInternalTypes.js
+3 -1
@@ -434,6 +434,8 @@ export type Dispatcher = {
434 ) => [Awaited<S>, (P) => void, boolean],
435 };
436
437 -export type CacheDispatcher = {
437 +export type AsyncDispatcher = {
438 getCacheForType: <T>(resourceType: () => T) => T,
439 + // DEV-only (or !disableStringRefs)
440 + getOwner: () => null | Fiber | ReactComponentInfo,
441 };
packages/react-server/src/ReactFizzAsyncDispatcher.js new
+27
@@ -0,0 +1,27 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
11 +
12 +import {disableStringRefs} from 'shared/ReactFeatureFlags';
13 +
14 +function getCacheForType<T>(resourceType: () => T): T {
15 + throw new Error('Not implemented.');
16 +}
17 +
18 +export const DefaultAsyncDispatcher: AsyncDispatcher = ({
19 + getCacheForType,
20 +}: any);
21 +
22 +if (__DEV__ || !disableStringRefs) {
23 + // Fizz never tracks owner but the JSX runtime looks for this.
24 + DefaultAsyncDispatcher.getOwner = (): null => {
25 + return null;
26 + };
27 +}
packages/react-server/src/ReactFizzCache.js deleted
-18
@@ -1,18 +0,0 @@
1 -/**
2 - * Copyright (c) Meta Platforms, Inc. and affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import type {CacheDispatcher} from 'react-reconciler/src/ReactInternalTypes';
11 -
12 -function getCacheForType<T>(resourceType: () => T): T {
13 - throw new Error('Not implemented.');
14 -}
15 -
16 -export const DefaultCacheDispatcher: CacheDispatcher = {
17 - getCacheForType,
18 -};
packages/react-server/src/ReactFizzServer.js
+7 -6
@@ -111,7 +111,7 @@ import {
111 getActionStateCount,
112 getActionStateMatchingIndex,
113 } from './ReactFizzHooks';
114 -import {DefaultCacheDispatcher} from './ReactFizzCache';
114 +import {DefaultAsyncDispatcher} from './ReactFizzAsyncDispatcher';
115 import {getStackByComponentStackNode} from './ReactFizzComponentStack';
116 import {emptyTreeContext, pushTreeContext} from './ReactFizzTreeContext';
117
@@ -148,6 +148,7 @@ import {
148 enableRefAsProp,
149 disableDefaultPropsExceptForClasses,
150 enableAsyncIterableChildren,
151 + disableStringRefs,
152 } from 'shared/ReactFeatureFlags';
153
154 import assign from 'shared/assign';
@@ -3791,10 +3792,10 @@ export function performWork(request: Request): void {
3792 const prevContext = getActiveContext();
3793 const prevDispatcher = ReactSharedInternals.H;
3794 ReactSharedInternals.H = HooksDispatcher;
3794 - let prevCacheDispatcher = null;
3795 - if (enableCache) {
3796 - prevCacheDispatcher = ReactSharedInternals.C;
3797 - ReactSharedInternals.C = DefaultCacheDispatcher;
3795 + let prevAsyncDispatcher = null;
3796 + if (enableCache || __DEV__ || !disableStringRefs) {
3797 + prevAsyncDispatcher = ReactSharedInternals.A;
3798 + ReactSharedInternals.A = DefaultAsyncDispatcher;
3799 }
3800
3801 const prevRequest = currentRequest;
@@ -3826,7 +3827,7 @@ export function performWork(request: Request): void {
3827 setCurrentResumableState(prevResumableState);
3828 ReactSharedInternals.H = prevDispatcher;
3829 if (enableCache) {
3829 - ReactSharedInternals.C = prevCacheDispatcher;
3830 + ReactSharedInternals.A = prevAsyncDispatcher;
3831 }
3832
3833 if (__DEV__) {
packages/react-server/src/ReactFlightServer.js
+11 -7
@@ -89,7 +89,11 @@ import {
89 getThenableStateAfterSuspending,
90 resetHooksForRequest,
91 } from './ReactFlightHooks';
92 -import {DefaultCacheDispatcher} from './flight/ReactFlightServerCache';
92 +import {
93 + DefaultAsyncDispatcher,
94 + currentOwner,
95 + setCurrentOwner,
96 +} from './flight/ReactFlightAsyncDispatcher';
97
98 import {
99 getIteratorFn,
@@ -158,7 +162,7 @@ function patchConsole(consoleInst: typeof console, methodName: string) {
162 // We don't currently use this id for anything but we emit it so that we can later
163 // refer to previous logs in debug info to associate them with a component.
164 const id = request.nextChunkId++;
161 - const owner: null | ReactComponentInfo = ReactSharedInternals.owner;
165 + const owner: null | ReactComponentInfo = currentOwner;
166 emitConsoleChunk(request, id, methodName, owner, stack, arguments);
167 }
168 // $FlowFixMe[prop-missing]
@@ -360,14 +364,14 @@ export function createRequest(
364 environmentName: void | string,
365 ): Request {
366 if (
363 - ReactSharedInternals.C !== null &&
364 - ReactSharedInternals.C !== DefaultCacheDispatcher
367 + ReactSharedInternals.A !== null &&
368 + ReactSharedInternals.A !== DefaultAsyncDispatcher
369 ) {
370 throw new Error(
371 'Currently React only supports one RSC renderer at a time.',
372 );
373 }
370 - ReactSharedInternals.C = DefaultCacheDispatcher;
374 + ReactSharedInternals.A = DefaultAsyncDispatcher;
375
376 const abortSet: Set<Task> = new Set();
377 const pingedTasks: Array<Task> = [];
@@ -856,11 +860,11 @@ function renderFunctionComponent<Props>(
860 const secondArg = undefined;
861 let result;
862 if (__DEV__) {
859 - ReactSharedInternals.owner = componentDebugInfo;
863 + setCurrentOwner(componentDebugInfo);
864 try {
865 result = Component(props, secondArg);
866 } finally {
863 - ReactSharedInternals.owner = null;
867 + setCurrentOwner(null);
868 }
869 } else {
870 result = Component(props, secondArg);
packages/react-server/src/flight/ReactFlightAsyncDispatcher.js new
+54
@@ -0,0 +1,54 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +import type {ReactComponentInfo} from 'shared/ReactTypes';
11 +
12 +import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
13 +
14 +import {resolveRequest, getCache} from '../ReactFlightServer';
15 +
16 +import {disableStringRefs} from 'shared/ReactFeatureFlags';
17 +
18 +function resolveCache(): Map<Function, mixed> {
19 + const request = resolveRequest();
20 + if (request) {
21 + return getCache(request);
22 + }
23 + return new Map();
24 +}
25 +
26 +export const DefaultAsyncDispatcher: AsyncDispatcher = ({
27 + getCacheForType<T>(resourceType: () => T): T {
28 + const cache = resolveCache();
29 + let entry: T | void = (cache.get(resourceType): any);
30 + if (entry === undefined) {
31 + entry = resourceType();
32 + // TODO: Warn if undefined?
33 + cache.set(resourceType, entry);
34 + }
35 + return entry;
36 + },
37 +}: any);
38 +
39 +export let currentOwner: ReactComponentInfo | null = null;
40 +
41 +if (__DEV__) {
42 + DefaultAsyncDispatcher.getOwner = (): null | ReactComponentInfo => {
43 + return currentOwner;
44 + };
45 +} else if (!disableStringRefs) {
46 + // Server Components never use string refs but the JSX runtime looks for it.
47 + DefaultAsyncDispatcher.getOwner = (): null | ReactComponentInfo => {
48 + return null;
49 + };
50 +}
51 +
52 +export function setCurrentOwner(componentInfo: null | ReactComponentInfo) {
53 + currentOwner = componentInfo;
54 +}
packages/react-server/src/flight/ReactFlightServerCache.js deleted
-33
@@ -1,33 +0,0 @@
1 -/**
2 - * Copyright (c) Meta Platforms, Inc. and affiliates.
3 - *
4 - * This source code is licensed under the MIT license found in the
5 - * LICENSE file in the root directory of this source tree.
6 - *
7 - * @flow
8 - */
9 -
10 -import type {CacheDispatcher} from 'react-reconciler/src/ReactInternalTypes';
11 -
12 -import {resolveRequest, getCache} from '../ReactFlightServer';
13 -
14 -function resolveCache(): Map<Function, mixed> {
15 - const request = resolveRequest();
16 - if (request) {
17 - return getCache(request);
18 - }
19 - return new Map();
20 -}
21 -
22 -export const DefaultCacheDispatcher: CacheDispatcher = {
23 - getCacheForType<T>(resourceType: () => T): T {
24 - const cache = resolveCache();
25 - let entry: T | void = (cache.get(resourceType): any);
26 - if (entry === undefined) {
27 - entry = resourceType();
28 - // TODO: Warn if undefined?
29 - cache.set(resourceType, entry);
30 - }
31 - return entry;
32 - },
33 -};
packages/react-suspense-test-utils/src/ReactSuspenseTestUtils.js
+8 -5
@@ -7,12 +7,12 @@
7 * @flow
8 */
9
10 -import type {CacheDispatcher} from 'react-reconciler/src/ReactInternalTypes';
10 +import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
11 import ReactSharedInternals from 'shared/ReactSharedInternals';
12
13 export function waitForSuspense<T>(fn: () => T): Promise<T> {
14 const cache: Map<Function, mixed> = new Map();
15 - const testDispatcher: CacheDispatcher = {
15 + const testDispatcher: AsyncDispatcher = {
16 getCacheForType<R>(resourceType: () => R): R {
17 let entry: R | void = (cache.get(resourceType): any);
18 if (entry === undefined) {
@@ -22,12 +22,15 @@ export function waitForSuspense<T>(fn: () => T): Promise<T> {
22 }
23 return entry;
24 },
25 + getOwner(): null {
26 + return null;
27 + },
28 };
29 // Not using async/await because we don't compile it.
30 return new Promise((resolve, reject) => {
31 function retry() {
29 - const prevDispatcher = ReactSharedInternals.C;
30 - ReactSharedInternals.C = testDispatcher;
32 + const prevDispatcher = ReactSharedInternals.A;
33 + ReactSharedInternals.A = testDispatcher;
34 try {
35 const result = fn();
36 resolve(result);
@@ -38,7 +41,7 @@ export function waitForSuspense<T>(fn: () => T): Promise<T> {
41 reject(thrownValue);
42 }
43 } finally {
41 - ReactSharedInternals.C = prevDispatcher;
44 + ReactSharedInternals.A = prevDispatcher;
45 }
46 }
47 retry();
packages/react/src/ReactCacheImpl.js
+1 -1
@@ -54,7 +54,7 @@ function createCacheNode<T>(): CacheNode<T> {
54
55 export function cache<A: Iterable<mixed>, T>(fn: (...A) => T): (...A) => T {
56 return function () {
57 - const dispatcher = ReactSharedInternals.C;
57 + const dispatcher = ReactSharedInternals.A;
58 if (!dispatcher) {
59 // If there is no dispatcher, then we treat this as not being cached.
60 // $FlowFixMe[incompatible-call]: We don't want to use rest arguments since we transpile the code.
packages/react/src/ReactHooks.js
+1 -1
@@ -44,7 +44,7 @@ function resolveDispatcher() {
44 }
45
46 export function getCacheForType<T>(resourceType: () => T): T {
47 - const dispatcher = ReactSharedInternals.C;
47 + const dispatcher = ReactSharedInternals.A;
48 if (!dispatcher) {
49 // If there is no dispatcher, then we treat this as not being cached.
50 return resourceType();
packages/react/src/ReactSharedInternalsClient.js
+4 -12
@@ -8,19 +8,15 @@
8 */
9
10 import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
11 -import type {CacheDispatcher} from 'react-reconciler/src/ReactInternalTypes';
11 +import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
12 import type {BatchConfigTransition} from 'react-reconciler/src/ReactFiberTracingMarkerComponent';
13 -import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
14 -
15 -import {disableStringRefs} from 'shared/ReactFeatureFlags';
13
14 export type SharedStateClient = {
15 H: null | Dispatcher, // ReactCurrentDispatcher for Hooks
19 - C: null | CacheDispatcher, // ReactCurrentCache for Cache
16 + A: null | AsyncDispatcher, // ReactCurrentCache for Cache
17 T: null | BatchConfigTransition, // ReactCurrentBatchConfig for Transitions
18
22 - // DEV-only-ish
23 - owner: null | Fiber, // ReactCurrentOwner is Fiber on the Client, null in Fizz. Flight uses SharedStateServer.
19 + // DEV-only
20
21 // ReactCurrentActQueue
22 actQueue: null | Array<RendererTask>,
@@ -47,14 +43,10 @@ export type RendererTask = boolean => RendererTask | null;
43
44 const ReactSharedInternals: SharedStateClient = ({
45 H: null,
50 - C: null,
46 + A: null,
47 T: null,
48 }: any);
49
54 -if (__DEV__ || !disableStringRefs) {
55 - ReactSharedInternals.owner = null;
56 -}
57 -
50 if (__DEV__) {
51 ReactSharedInternals.actQueue = null;
52 ReactSharedInternals.isBatchingLegacy = false;
packages/react/src/ReactSharedInternalsServer.js
+5 -11
@@ -8,8 +8,7 @@
8 */
9
10 import type {Dispatcher} from 'react-reconciler/src/ReactInternalTypes';
11 -import type {CacheDispatcher} from 'react-reconciler/src/ReactInternalTypes';
12 -import type {ReactComponentInfo} from 'shared/ReactTypes';
11 +import type {AsyncDispatcher} from 'react-reconciler/src/ReactInternalTypes';
12
13 import type {
14 Reference,
@@ -24,11 +23,11 @@ import {
23 TaintRegistryPendingRequests,
24 } from './ReactTaintRegistry';
25
27 -import {disableStringRefs, enableTaint} from 'shared/ReactFeatureFlags';
26 +import {enableTaint} from 'shared/ReactFeatureFlags';
27
28 export type SharedStateServer = {
29 H: null | Dispatcher, // ReactCurrentDispatcher for Hooks
31 - C: null | CacheDispatcher, // ReactCurrentCache for Cache
30 + A: null | AsyncDispatcher, // ReactCurrentCache for Cache
31
32 // enableTaint
33 TaintRegistryObjects: WeakMap<Reference, string>,
@@ -36,8 +35,7 @@ export type SharedStateServer = {
35 TaintRegistryByteLengths: Set<number>,
36 TaintRegistryPendingRequests: Set<RequestCleanupQueue>,
37
39 - // DEV-only-ish
40 - owner: null | ReactComponentInfo, // ReactCurrentOwner is ReactComponentInfo in Flight, null in Fizz. Fiber/Fizz uses SharedStateClient.
38 + // DEV-only
39
40 // ReactDebugCurrentFrame
41 setExtraStackFrame: (stack: null | string) => void,
@@ -49,7 +47,7 @@ export type RendererTask = boolean => RendererTask | null;
47
48 const ReactSharedInternals: SharedStateServer = ({
49 H: null,
52 - C: null,
50 + A: null,
51 }: any);
52
53 if (enableTaint) {
@@ -60,10 +58,6 @@ if (enableTaint) {
58 TaintRegistryPendingRequests;
59 }
60
63 -if (__DEV__ || !disableStringRefs) {
64 - ReactSharedInternals.owner = null;
65 -}
66 -
61 if (__DEV__) {
62 let currentExtraStackFrame = (null: null | string);
63 ReactSharedInternals.setExtraStackFrame = function (stack: null | string) {
packages/react/src/jsx/ReactJSXElement.js
+30 -43
@@ -29,6 +29,17 @@ import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFrom
29
30 const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference');
31
32 +function getOwner() {
33 + if (__DEV__ || !disableStringRefs) {
34 + const dispatcher = ReactSharedInternals.A;
35 + if (dispatcher === null) {
36 + return null;
37 + }
38 + return dispatcher.getOwner();
39 + }
40 + return null;
41 +}
42 +
43 let specialPropKeyWarningShown;
44 let specialPropRefWarningShown;
45 let didWarnAboutStringRefs;
@@ -66,16 +77,15 @@ function hasValidKey(config) {
77
78 function warnIfStringRefCannotBeAutoConverted(config, self) {
79 if (__DEV__) {
80 + let owner;
81 if (
82 !disableStringRefs &&
83 typeof config.ref === 'string' &&
72 - ReactSharedInternals.owner &&
84 + (owner = getOwner()) &&
85 self &&
74 - ReactSharedInternals.owner.stateNode !== self
86 + owner.stateNode !== self
87 ) {
76 - const componentName = getComponentNameFromType(
77 - ReactSharedInternals.owner.type,
78 - );
88 + const componentName = getComponentNameFromType(owner.type);
89
90 if (!didWarnAboutStringRefs[componentName]) {
91 console.error(
@@ -85,7 +95,7 @@ function warnIfStringRefCannotBeAutoConverted(config, self) {
95 'We ask you to manually fix this case by using useRef() or createRef() instead. ' +
96 'Learn more about using refs safely here: ' +
97 'https://react.dev/link/strict-mode-string-ref',
88 - getComponentNameFromType(ReactSharedInternals.owner.type),
98 + getComponentNameFromType(owner.type),
99 config.ref,
100 );
101 didWarnAboutStringRefs[componentName] = true;
@@ -339,7 +349,7 @@ export function jsxProd(type, config, maybeKey) {
349 if (!enableRefAsProp) {
350 ref = config.ref;
351 if (!disableStringRefs) {
342 - ref = coerceStringRef(ref, ReactSharedInternals.owner, type);
352 + ref = coerceStringRef(ref, getOwner(), type);
353 }
354 }
355 }
@@ -365,11 +375,7 @@ export function jsxProd(type, config, maybeKey) {
375 // Skip over reserved prop names
376 if (propName !== 'key' && (enableRefAsProp || propName !== 'ref')) {
377 if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
368 - props.ref = coerceStringRef(
369 - config[propName],
370 - ReactSharedInternals.owner,
371 - type,
372 - );
378 + props.ref = coerceStringRef(config[propName], getOwner(), type);
379 } else {
380 props[propName] = config[propName];
381 }
@@ -389,15 +395,7 @@ export function jsxProd(type, config, maybeKey) {
395 }
396 }
397
392 - return ReactElement(
393 - type,
394 - key,
395 - ref,
396 - undefined,
397 - undefined,
398 - ReactSharedInternals.owner,
399 - props,
400 - );
398 + return ReactElement(type, key, ref, undefined, undefined, getOwner(), props);
399 }
400
401 // While `jsxDEV` should never be called when running in production, we do
@@ -571,7 +569,7 @@ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
569 if (!enableRefAsProp) {
570 ref = config.ref;
571 if (!disableStringRefs) {
574 - ref = coerceStringRef(ref, ReactSharedInternals.owner, type);
572 + ref = coerceStringRef(ref, getOwner(), type);
573 }
574 }
575 if (!disableStringRefs) {
@@ -600,11 +598,7 @@ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
598 // Skip over reserved prop names
599 if (propName !== 'key' && (enableRefAsProp || propName !== 'ref')) {
600 if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
603 - props.ref = coerceStringRef(
604 - config[propName],
605 - ReactSharedInternals.owner,
606 - type,
607 - );
601 + props.ref = coerceStringRef(config[propName], getOwner(), type);
602 } else {
603 props[propName] = config[propName];
604 }
@@ -643,7 +637,7 @@ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
637 ref,
638 self,
639 source,
646 - ReactSharedInternals.owner,
640 + getOwner(),
641 props,
642 );
643
@@ -747,7 +741,7 @@ export function createElement(type, config, children) {
741 if (!enableRefAsProp) {
742 ref = config.ref;
743 if (!disableStringRefs) {
750 - ref = coerceStringRef(ref, ReactSharedInternals.owner, type);
744 + ref = coerceStringRef(ref, getOwner(), type);
745 }
746 }
747
@@ -777,11 +771,7 @@ export function createElement(type, config, children) {
771 propName !== '__source'
772 ) {
773 if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
780 - props.ref = coerceStringRef(
781 - config[propName],
782 - ReactSharedInternals.owner,
783 - type,
784 - );
774 + props.ref = coerceStringRef(config[propName], getOwner(), type);
775 } else {
776 props[propName] = config[propName];
777 }
@@ -837,7 +827,7 @@ export function createElement(type, config, children) {
827 ref,
828 undefined,
829 undefined,
840 - ReactSharedInternals.owner,
830 + getOwner(),
831 props,
832 );
833
@@ -887,7 +877,7 @@ export function cloneElement(element, config, children) {
877
878 if (config != null) {
879 if (hasValidRef(config)) {
890 - owner = ReactSharedInternals.owner;
880 + owner = __DEV__ || !disableStringRefs ? getOwner() : undefined;
881 if (!enableRefAsProp) {
882 // Silently steal the ref from the parent.
883 ref = config.ref;
@@ -981,8 +971,9 @@ export function cloneElement(element, config, children) {
971
972 function getDeclarationErrorAddendum() {
973 if (__DEV__) {
984 - if (ReactSharedInternals.owner) {
985 - const name = getComponentNameFromType(ReactSharedInternals.owner.type);
974 + const owner = getOwner();
975 + if (owner) {
976 + const name = getComponentNameFromType(owner.type);
977 if (name) {
978 return '\n\nCheck the render method of `' + name + '`.';
979 }
@@ -1085,11 +1076,7 @@ function validateExplicitKey(element, parentType) {
1076 // property, it may be the creator of the child that's responsible for
1077 // assigning it a key.
1078 let childOwner = '';
1088 - if (
1089 - element &&
1090 - element._owner != null &&
1091 - element._owner !== ReactSharedInternals.owner
1092 - ) {
1079 + if (element && element._owner != null && element._owner !== getOwner()) {
1080 let ownerName = null;
1081 if (typeof element._owner.tag === 'number') {
1082 ownerName = getComponentNameFromType(element._owner.type);