Skip to content

Commit 94eed63

Browse files
gnoffsebmarkbage
andauthored
(Land #28798) Move Current Owner (and Cache) to an Async Dispatcher (#28912)
Rebasing and landing #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>
1 parent f8a8eac commit 94eed63

21 files changed

+231
-176
lines changed

packages/react-dom/src/__tests__/ReactCompositeComponent-test.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -537,16 +537,24 @@ describe('ReactCompositeComponent', () => {
537537
});
538538

539539
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;
540547
class BadComponent extends React.Component {
541548
render() {
549+
// Stash the dispatcher that was available in render so we can check
550+
// that its internals also reset.
551+
stashedDispatcher = ReactSharedInternals.A;
542552
throw new Error();
543553
}
544554
}
545555

546556
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);
550558

551559
const root = ReactDOMClient.createRoot(document.createElement('div'));
552560
await expect(async () => {
@@ -555,9 +563,16 @@ describe('ReactCompositeComponent', () => {
555563
});
556564
}).rejects.toThrow();
557565

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+
}
561576
});
562577

563578
it('should call componentWillUnmount before unmounting', async () => {

packages/react-dom/src/client/ReactDOMRootFB.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ import {LegacyRoot} from 'react-reconciler/src/ReactRootTags';
6161
import getComponentNameFromType from 'shared/getComponentNameFromType';
6262
import {has as hasInstance} from 'shared/ReactInstanceMap';
6363

64-
import ReactSharedInternals from 'shared/ReactSharedInternals';
64+
import {currentOwner} from 'react-reconciler/src/ReactFiberCurrentOwner';
6565

6666
import assign from 'shared/assign';
6767

@@ -342,7 +342,7 @@ export function findDOMNode(
342342
componentOrElement: Element | ?React$Component<any, any>,
343343
): null | Element | Text {
344344
if (__DEV__) {
345-
const owner = (ReactSharedInternals.owner: any);
345+
const owner = currentOwner;
346346
if (owner !== null && owner.stateNode !== null) {
347347
const warnedAboutRefsInRender = owner.stateNode._warnedAboutRefsInRender;
348348
if (!warnedAboutRefsInRender) {

packages/react-native-renderer/src/ReactNativePublicCompat.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ import {
2424
findHostInstanceWithWarning,
2525
} from 'react-reconciler/src/ReactFiberReconciler';
2626
import {doesFiberContain} from 'react-reconciler/src/ReactFiberTreeReflection';
27-
import ReactSharedInternals from 'shared/ReactSharedInternals';
2827
import getComponentNameFromType from 'shared/getComponentNameFromType';
28+
import {currentOwner} from 'react-reconciler/src/ReactFiberCurrentOwner';
2929

3030
export function findHostInstance_DEPRECATED<TElementType: ElementType>(
3131
componentOrHandle: ?(ElementRef<TElementType> | number),
3232
): ?ElementRef<HostComponent<mixed>> {
3333
if (__DEV__) {
34-
const owner = ReactSharedInternals.owner;
34+
const owner = currentOwner;
3535
if (owner !== null && owner.stateNode !== null) {
3636
if (!owner.stateNode._warnedAboutRefsInRender) {
3737
console.error(
@@ -86,7 +86,7 @@ export function findHostInstance_DEPRECATED<TElementType: ElementType>(
8686

8787
export function findNodeHandle(componentOrHandle: any): ?number {
8888
if (__DEV__) {
89-
const owner = ReactSharedInternals.owner;
89+
const owner = currentOwner;
9090
if (owner !== null && owner.stateNode !== null) {
9191
if (!owner.stateNode._warnedAboutRefsInRender) {
9292
console.error(

packages/react-reconciler/src/ReactFiberCache.js renamed to packages/react-reconciler/src/ReactFiberAsyncDispatcher.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
* @flow
88
*/
99

10-
import type {CacheDispatcher} from './ReactInternalTypes';
10+
import type {AsyncDispatcher, Fiber} from './ReactInternalTypes';
1111
import type {Cache} from './ReactFiberCacheComponent';
1212

1313
import {enableCache} from 'shared/ReactFeatureFlags';
1414
import {readContext} from './ReactFiberNewContext';
1515
import {CacheContext} from './ReactFiberCacheComponent';
1616

17+
import {disableStringRefs} from 'shared/ReactFeatureFlags';
18+
19+
import {currentOwner} from './ReactFiberCurrentOwner';
20+
1721
function getCacheForType<T>(resourceType: () => T): T {
1822
if (!enableCache) {
1923
throw new Error('Not implemented.');
@@ -27,6 +31,12 @@ function getCacheForType<T>(resourceType: () => T): T {
2731
return cacheForType;
2832
}
2933

30-
export const DefaultCacheDispatcher: CacheDispatcher = {
34+
export const DefaultAsyncDispatcher: AsyncDispatcher = ({
3135
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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ import {
9191
Passive,
9292
DidDefer,
9393
} from './ReactFiberFlags';
94-
import ReactSharedInternals from 'shared/ReactSharedInternals';
9594
import {
9695
debugRenderPhaseSideEffectsForStrictMode,
9796
disableLegacyContext,
@@ -297,6 +296,7 @@ import {
297296
pushRootMarkerInstance,
298297
TransitionTracingMarker,
299298
} from './ReactFiberTracingMarkerComponent';
299+
import {setCurrentOwner} from './ReactFiberCurrentOwner';
300300

301301
// A special exception that's used to unwind the stack when an update flows
302302
// into a dehydrated boundary.
@@ -432,7 +432,7 @@ function updateForwardRef(
432432
markComponentRenderStarted(workInProgress);
433433
}
434434
if (__DEV__) {
435-
ReactSharedInternals.owner = workInProgress;
435+
setCurrentOwner(workInProgress);
436436
setIsRendering(true);
437437
nextChildren = renderWithHooks(
438438
current,
@@ -1150,7 +1150,7 @@ function updateFunctionComponent(
11501150
markComponentRenderStarted(workInProgress);
11511151
}
11521152
if (__DEV__) {
1153-
ReactSharedInternals.owner = workInProgress;
1153+
setCurrentOwner(workInProgress);
11541154
setIsRendering(true);
11551155
nextChildren = renderWithHooks(
11561156
current,
@@ -1373,7 +1373,7 @@ function finishClassComponent(
13731373

13741374
// Rerender
13751375
if (__DEV__ || !disableStringRefs) {
1376-
ReactSharedInternals.owner = workInProgress;
1376+
setCurrentOwner(workInProgress);
13771377
}
13781378
let nextChildren;
13791379
if (
@@ -3419,7 +3419,7 @@ function updateContextConsumer(
34193419
}
34203420
let newChildren;
34213421
if (__DEV__) {
3422-
ReactSharedInternals.owner = workInProgress;
3422+
setCurrentOwner(workInProgress);
34233423
setIsRendering(true);
34243424
newChildren = render(newValue);
34253425
setIsRendering(false);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import type {Container, SuspenseInstance} from './ReactFiberConfig';
1212
import type {SuspenseState} from './ReactFiberSuspenseComponent';
1313

1414
import {get as getInstance} from 'shared/ReactInstanceMap';
15-
import ReactSharedInternals from 'shared/ReactSharedInternals';
1615
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
1716
import {
1817
ClassComponent,
@@ -25,6 +24,7 @@ import {
2524
SuspenseComponent,
2625
} from './ReactWorkTags';
2726
import {NoFlags, Placement, Hydrating} from './ReactFiberFlags';
27+
import {currentOwner} from './ReactFiberCurrentOwner';
2828

2929
export function getNearestMountedFiber(fiber: Fiber): null | Fiber {
3030
let node = fiber;
@@ -89,7 +89,7 @@ export function isFiberMounted(fiber: Fiber): boolean {
8989

9090
export function isMounted(component: React$Component<any, any>): boolean {
9191
if (__DEV__) {
92-
const owner = (ReactSharedInternals.owner: any);
92+
const owner = currentOwner;
9393
if (owner !== null && owner.tag === ClassComponent) {
9494
const ownerFiber: Fiber = owner;
9595
const instance = ownerFiber.stateNode;

packages/react-reconciler/src/ReactFiberWorkLoop.js

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ import {
203203
resetHooksOnUnwind,
204204
ContextOnlyDispatcher,
205205
} from './ReactFiberHooks';
206-
import {DefaultCacheDispatcher} from './ReactFiberCache';
206+
import {DefaultAsyncDispatcher} from './ReactFiberAsyncDispatcher';
207+
import {setCurrentOwner} from './ReactFiberCurrentOwner';
207208
import {
208209
createCapturedValueAtFiber,
209210
type CapturedValue,
@@ -1684,7 +1685,7 @@ function handleThrow(root: FiberRoot, thrownValue: any): void {
16841685
resetHooksAfterThrow();
16851686
resetCurrentDebugFiberInDEV();
16861687
if (__DEV__ || !disableStringRefs) {
1687-
ReactSharedInternals.owner = null;
1688+
setCurrentOwner(null);
16881689
}
16891690

16901691
if (thrownValue === SuspenseException) {
@@ -1874,19 +1875,19 @@ function popDispatcher(prevDispatcher: any) {
18741875
ReactSharedInternals.H = prevDispatcher;
18751876
}
18761877

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;
18821883
} else {
18831884
return null;
18841885
}
18851886
}
18861887

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;
18901891
}
18911892
}
18921893

@@ -1963,7 +1964,7 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
19631964
const prevExecutionContext = executionContext;
19641965
executionContext |= RenderContext;
19651966
const prevDispatcher = pushDispatcher(root.containerInfo);
1966-
const prevCacheDispatcher = pushCacheDispatcher();
1967+
const prevAsyncDispatcher = pushAsyncDispatcher();
19671968

19681969
// If the root or lanes have changed, throw out the existing stack
19691970
// and prepare a fresh one. Otherwise we'll continue where we left off.
@@ -2061,7 +2062,7 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
20612062

20622063
executionContext = prevExecutionContext;
20632064
popDispatcher(prevDispatcher);
2064-
popCacheDispatcher(prevCacheDispatcher);
2065+
popAsyncDispatcher(prevAsyncDispatcher);
20652066

20662067
if (workInProgress !== null) {
20672068
// This is a sync render, so we should have finished the whole tree.
@@ -2104,7 +2105,7 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
21042105
const prevExecutionContext = executionContext;
21052106
executionContext |= RenderContext;
21062107
const prevDispatcher = pushDispatcher(root.containerInfo);
2107-
const prevCacheDispatcher = pushCacheDispatcher();
2108+
const prevAsyncDispatcher = pushAsyncDispatcher();
21082109

21092110
// If the root or lanes have changed, throw out the existing stack
21102111
// and prepare a fresh one. Otherwise we'll continue where we left off.
@@ -2317,7 +2318,7 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
23172318
resetContextDependencies();
23182319

23192320
popDispatcher(prevDispatcher);
2320-
popCacheDispatcher(prevCacheDispatcher);
2321+
popAsyncDispatcher(prevAsyncDispatcher);
23212322
executionContext = prevExecutionContext;
23222323

23232324
if (__DEV__) {
@@ -2386,7 +2387,7 @@ function performUnitOfWork(unitOfWork: Fiber): void {
23862387
}
23872388

23882389
if (__DEV__ || !disableStringRefs) {
2389-
ReactSharedInternals.owner = null;
2390+
setCurrentOwner(null);
23902391
}
23912392
}
23922393

@@ -2501,7 +2502,7 @@ function replaySuspendedUnitOfWork(unitOfWork: Fiber): void {
25012502
}
25022503

25032504
if (__DEV__ || !disableStringRefs) {
2504-
ReactSharedInternals.owner = null;
2505+
setCurrentOwner(null);
25052506
}
25062507
}
25072508

@@ -2894,7 +2895,7 @@ function commitRootImpl(
28942895

28952896
// Reset this to null before calling lifecycles
28962897
if (__DEV__ || !disableStringRefs) {
2897-
ReactSharedInternals.owner = null;
2898+
setCurrentOwner(null);
28982899
}
28992900

29002901
// The commit phase is broken into several sub-phases. We do a separate pass

packages/react-reconciler/src/ReactInternalTypes.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,8 @@ export type Dispatcher = {
434434
) => [Awaited<S>, (P) => void, boolean],
435435
};
436436

437-
export type CacheDispatcher = {
437+
export type AsyncDispatcher = {
438438
getCacheForType: <T>(resourceType: () => T) => T,
439+
// DEV-only (or !disableStringRefs)
440+
getOwner: () => null | Fiber | ReactComponentInfo,
439441
};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

0 commit comments

Comments
 (0)