(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);