Skip to content

Commit d50323e

Browse files
authored
Flatten ReactSharedInternals (#28783)
This is similar to #28771 but for isomorphic. We need a make over for these dispatchers anyway so this is the first step. Also helps flush out some internals usage that will break anyway. It flattens the inner mutable objects onto the ReactSharedInternals.
1 parent acef2f8 commit d50323e

File tree

65 files changed

+652
-791
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+652
-791
lines changed

packages/react-cache/src/ReactCacheOld.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,11 @@ const Pending = 0;
4444
const Resolved = 1;
4545
const Rejected = 2;
4646

47-
const ReactCurrentDispatcher =
48-
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
49-
.ReactCurrentDispatcher;
47+
const SharedInternals =
48+
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
5049

5150
function readContext(Context: ReactContext<mixed>) {
52-
const dispatcher = ReactCurrentDispatcher.current;
51+
const dispatcher = SharedInternals.H;
5352
if (dispatcher === null) {
5453
// This wasn't being minified but we're going to retire this package anyway.
5554
// eslint-disable-next-line react-internal/prod-error-codes

packages/react-debug-tools/src/ReactDebugHooks.js

+9-9
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {
3737
} from 'shared/ReactSymbols';
3838
import hasOwnProperty from 'shared/hasOwnProperty';
3939

40-
type CurrentDispatcherRef = typeof ReactSharedInternals.ReactCurrentDispatcher;
40+
type CurrentDispatcherRef = typeof ReactSharedInternals;
4141

4242
// Used to track hooks called during a render
4343

@@ -1075,11 +1075,11 @@ export function inspectHooks<Props>(
10751075
// DevTools will pass the current renderer's injected dispatcher.
10761076
// Other apps might compile debug hooks as part of their app though.
10771077
if (currentDispatcher == null) {
1078-
currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
1078+
currentDispatcher = ReactSharedInternals;
10791079
}
10801080

1081-
const previousDispatcher = currentDispatcher.current;
1082-
currentDispatcher.current = DispatcherProxy;
1081+
const previousDispatcher = currentDispatcher.H;
1082+
currentDispatcher.H = DispatcherProxy;
10831083

10841084
let readHookLog;
10851085
let ancestorStackError;
@@ -1093,7 +1093,7 @@ export function inspectHooks<Props>(
10931093
readHookLog = hookLog;
10941094
hookLog = [];
10951095
// $FlowFixMe[incompatible-use] found when upgrading Flow
1096-
currentDispatcher.current = previousDispatcher;
1096+
currentDispatcher.H = previousDispatcher;
10971097
}
10981098
const rootStack = ErrorStackParser.parse(ancestorStackError);
10991099
return buildTree(rootStack, readHookLog);
@@ -1129,9 +1129,9 @@ function inspectHooksOfForwardRef<Props, Ref>(
11291129
ref: Ref,
11301130
currentDispatcher: CurrentDispatcherRef,
11311131
): HooksTree {
1132-
const previousDispatcher = currentDispatcher.current;
1132+
const previousDispatcher = currentDispatcher.H;
11331133
let readHookLog;
1134-
currentDispatcher.current = DispatcherProxy;
1134+
currentDispatcher.H = DispatcherProxy;
11351135
let ancestorStackError;
11361136
try {
11371137
ancestorStackError = new Error();
@@ -1141,7 +1141,7 @@ function inspectHooksOfForwardRef<Props, Ref>(
11411141
} finally {
11421142
readHookLog = hookLog;
11431143
hookLog = [];
1144-
currentDispatcher.current = previousDispatcher;
1144+
currentDispatcher.H = previousDispatcher;
11451145
}
11461146
const rootStack = ErrorStackParser.parse(ancestorStackError);
11471147
return buildTree(rootStack, readHookLog);
@@ -1169,7 +1169,7 @@ export function inspectHooksOfFiber(
11691169
// DevTools will pass the current renderer's injected dispatcher.
11701170
// Other apps might compile debug hooks as part of their app though.
11711171
if (currentDispatcher == null) {
1172-
currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
1172+
currentDispatcher = ReactSharedInternals;
11731173
}
11741174

11751175
if (

packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.js

-33
Original file line numberDiff line numberDiff line change
@@ -453,39 +453,6 @@ describe('ReactHooksInspection', () => {
453453
`);
454454
});
455455

456-
it('should support an injected dispatcher', () => {
457-
const initial = {
458-
useState() {
459-
throw new Error("Should've been proxied");
460-
},
461-
};
462-
let current = initial;
463-
let getterCalls = 0;
464-
const setterCalls = [];
465-
const FakeDispatcherRef = {
466-
get current() {
467-
getterCalls++;
468-
return current;
469-
},
470-
set current(value) {
471-
setterCalls.push(value);
472-
current = value;
473-
},
474-
};
475-
476-
function Foo(props) {
477-
const [state] = FakeDispatcherRef.current.useState('hello world');
478-
return <div>{state}</div>;
479-
}
480-
481-
ReactDebugTools.inspectHooks(Foo, {}, FakeDispatcherRef);
482-
483-
expect(getterCalls).toBe(2);
484-
expect(setterCalls).toHaveLength(2);
485-
expect(setterCalls[0]).not.toBe(initial);
486-
expect(setterCalls[1]).toBe(initial);
487-
});
488-
489456
it('should inspect use() calls for Promise and Context', async () => {
490457
const MyContext = React.createContext('hi');
491458
const promise = Promise.resolve('world');

packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.js

-55
Original file line numberDiff line numberDiff line change
@@ -2345,61 +2345,6 @@ describe('ReactHooksInspectionIntegration', () => {
23452345
`);
23462346
});
23472347

2348-
it('should support an injected dispatcher', async () => {
2349-
function Foo(props) {
2350-
const [state] = React.useState('hello world');
2351-
return <div>{state}</div>;
2352-
}
2353-
2354-
const initial = {};
2355-
let current = initial;
2356-
let getterCalls = 0;
2357-
const setterCalls = [];
2358-
const FakeDispatcherRef = {
2359-
get current() {
2360-
getterCalls++;
2361-
return current;
2362-
},
2363-
set current(value) {
2364-
setterCalls.push(value);
2365-
current = value;
2366-
},
2367-
};
2368-
2369-
let renderer;
2370-
await act(() => {
2371-
renderer = ReactTestRenderer.create(<Foo />, {
2372-
unstable_isConcurrent: true,
2373-
});
2374-
});
2375-
const childFiber = renderer.root._currentFiber();
2376-
2377-
let didCatch = false;
2378-
2379-
try {
2380-
ReactDebugTools.inspectHooksOfFiber(childFiber, FakeDispatcherRef);
2381-
} catch (error) {
2382-
expect(error.message).toBe('Error rendering inspected component');
2383-
expect(error.cause).toBeInstanceOf(Error);
2384-
expect(error.cause.message).toBe(
2385-
'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
2386-
' one of the following reasons:\n' +
2387-
'1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
2388-
'2. You might be breaking the Rules of Hooks\n' +
2389-
'3. You might have more than one copy of React in the same app\n' +
2390-
'See https://react.dev/link/invalid-hook-call for tips about how to debug and fix this problem.',
2391-
);
2392-
didCatch = true;
2393-
}
2394-
// avoid false positive if no error was thrown at all
2395-
expect(didCatch).toBe(true);
2396-
2397-
expect(getterCalls).toBe(1);
2398-
expect(setterCalls).toHaveLength(2);
2399-
expect(setterCalls[0]).not.toBe(initial);
2400-
expect(setterCalls[1]).toBe(initial);
2401-
});
2402-
24032348
// This test case is based on an open source bug report:
24042349
// https://github.com/facebookincubator/redux-react-hook/issues/34#issuecomment-466693787
24052350
it('should properly advance the current hook for useContext', async () => {

packages/react-devtools-shared/src/backend/DevToolsComponentStackFrame.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ export function describeNativeComponentFrame(
8686
// Note that unlike the code this was forked from (in ReactComponentStackFrame)
8787
// DevTools should override the dispatcher even when DevTools is compiled in production mode,
8888
// because the app itself may be in development mode and log errors/warnings.
89-
const previousDispatcher = currentDispatcherRef.current;
90-
currentDispatcherRef.current = null;
89+
const previousDispatcher = currentDispatcherRef.H;
90+
currentDispatcherRef.H = null;
9191
disableLogs();
9292

9393
// NOTE: keep in sync with the implementation in ReactComponentStackFrame
@@ -270,7 +270,7 @@ export function describeNativeComponentFrame(
270270

271271
Error.prepareStackTrace = previousPrepareStackTrace;
272272

273-
currentDispatcherRef.current = previousDispatcher;
273+
currentDispatcherRef.H = previousDispatcher;
274274
reenableLogs();
275275
}
276276
// Fallback to just using the name if we couldn't make it throw.

packages/react-devtools-shared/src/backend/console.js

+7-9
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99

1010
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
1111
import type {
12+
LegacyDispatcherRef,
1213
CurrentDispatcherRef,
1314
ReactRenderer,
1415
WorkTagMap,
1516
ConsolePatchSettings,
1617
} from './types';
1718
import {format, formatWithStyles} from './utils';
1819

19-
import {getInternalReactConstants} from './renderer';
20+
import {getInternalReactConstants, getDispatcherRef} from './renderer';
2021
import {getStackByFiberInDevAndProd} from './DevToolsFiberComponentStack';
2122
import {consoleManagedByDevToolsDuringStrictMode} from 'react-devtools-feature-flags';
2223
import {castBool, castBrowserTheme} from '../utils';
@@ -75,7 +76,7 @@ type OnErrorOrWarning = (
7576
const injectedRenderers: Map<
7677
ReactRenderer,
7778
{
78-
currentDispatcherRef: CurrentDispatcherRef,
79+
currentDispatcherRef: LegacyDispatcherRef | CurrentDispatcherRef,
7980
getCurrentFiber: () => Fiber | null,
8081
onErrorOrWarning: ?OnErrorOrWarning,
8182
workTagMap: WorkTagMap,
@@ -215,12 +216,9 @@ export function patch({
215216
// Search for the first renderer that has a current Fiber.
216217
// We don't handle the edge case of stacks for more than one (e.g. interleaved renderers?)
217218
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
218-
for (const {
219-
currentDispatcherRef,
220-
getCurrentFiber,
221-
onErrorOrWarning,
222-
workTagMap,
223-
} of injectedRenderers.values()) {
219+
for (const renderer of injectedRenderers.values()) {
220+
const currentDispatcherRef = getDispatcherRef(renderer);
221+
const {getCurrentFiber, onErrorOrWarning, workTagMap} = renderer;
224222
const current: ?Fiber = getCurrentFiber();
225223
if (current != null) {
226224
try {
@@ -241,7 +239,7 @@ export function patch({
241239
const componentStack = getStackByFiberInDevAndProd(
242240
workTagMap,
243241
current,
244-
currentDispatcherRef,
242+
(currentDispatcherRef: any),
245243
);
246244
if (componentStack !== '') {
247245
if (isStrictModeOverride(args, method)) {

packages/react-devtools-shared/src/backend/renderer.js

+30-6
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ import type {
119119
RendererInterface,
120120
SerializedElement,
121121
WorkTagMap,
122+
CurrentDispatcherRef,
123+
LegacyDispatcherRef,
122124
} from './types';
123125
import type {
124126
ComponentFilter,
@@ -140,6 +142,31 @@ type ReactPriorityLevelsType = {
140142
NoPriority: number,
141143
};
142144

145+
export function getDispatcherRef(renderer: {
146+
+currentDispatcherRef?: LegacyDispatcherRef | CurrentDispatcherRef,
147+
...
148+
}): void | CurrentDispatcherRef {
149+
if (renderer.currentDispatcherRef === undefined) {
150+
return undefined;
151+
}
152+
const injectedRef = renderer.currentDispatcherRef;
153+
if (
154+
typeof injectedRef.H === 'undefined' &&
155+
typeof injectedRef.current !== 'undefined'
156+
) {
157+
// We got a legacy dispatcher injected, let's create a wrapper proxy to translate.
158+
return {
159+
get H() {
160+
return (injectedRef: any).current;
161+
},
162+
set H(value) {
163+
(injectedRef: any).current = value;
164+
},
165+
};
166+
}
167+
return (injectedRef: any);
168+
}
169+
143170
function getFiberFlags(fiber: Fiber): number {
144171
// The name of this field changed from "effectTag" to "flags"
145172
return fiber.flags !== undefined ? fiber.flags : (fiber: any).effectTag;
@@ -694,7 +721,7 @@ export function attach(
694721
getDisplayNameForFiber,
695722
getIsProfiling: () => isProfiling,
696723
getLaneLabelMap,
697-
currentDispatcherRef: renderer.currentDispatcherRef,
724+
currentDispatcherRef: getDispatcherRef(renderer),
698725
workTagMap: ReactTypeOfWork,
699726
reactVersion: version,
700727
});
@@ -3344,10 +3371,7 @@ export function attach(
33443371
}
33453372

33463373
try {
3347-
hooks = inspectHooksOfFiber(
3348-
fiber,
3349-
(renderer.currentDispatcherRef: any),
3350-
);
3374+
hooks = inspectHooksOfFiber(fiber, getDispatcherRef(renderer));
33513375
} finally {
33523376
// Restore original console functionality.
33533377
for (const method in originalConsoleMethods) {
@@ -4571,7 +4595,7 @@ export function attach(
45714595
function getComponentStackForFiber(fiber: Fiber): string | null {
45724596
let componentStack = fiberToComponentStackMap.get(fiber);
45734597
if (componentStack == null) {
4574-
const dispatcherRef = renderer.currentDispatcherRef;
4598+
const dispatcherRef = getDispatcherRef(renderer);
45754599
if (dispatcherRef == null) {
45764600
return null;
45774601
}

packages/react-devtools-shared/src/backend/types.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,12 @@ export type NativeType = Object;
8787
export type RendererID = number;
8888

8989
type Dispatcher = any;
90-
export type CurrentDispatcherRef = {current: null | Dispatcher};
90+
export type LegacyDispatcherRef = {current: null | Dispatcher};
91+
type SharedInternalsSubset = {
92+
H: null | Dispatcher,
93+
...
94+
};
95+
export type CurrentDispatcherRef = SharedInternalsSubset;
9196

9297
export type GetDisplayNameForFiberID = (
9398
id: number,
@@ -155,7 +160,7 @@ export type ReactRenderer = {
155160
scheduleUpdate?: ?(fiber: Object) => void,
156161
setSuspenseHandler?: ?(shouldSuspend: (fiber: Object) => boolean) => void,
157162
// Only injected by React v16.8+ in order to support hooks inspection.
158-
currentDispatcherRef?: CurrentDispatcherRef,
163+
currentDispatcherRef?: LegacyDispatcherRef | CurrentDispatcherRef,
159164
// Only injected by React v16.9+ in DEV mode.
160165
// Enables DevTools to append owners-only component stack to error messages.
161166
getCurrentFiber?: () => Fiber | null,

packages/react-devtools-shared/src/devtools/cache.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ const Pending = 0;
5959
const Resolved = 1;
6060
const Rejected = 2;
6161

62-
const ReactCurrentDispatcher = (React: any)
63-
.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher;
62+
const ReactSharedInternals = (React: any)
63+
.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
6464

6565
function readContext(Context: ReactContext<null>) {
66-
const dispatcher = ReactCurrentDispatcher.current;
66+
const dispatcher = ReactSharedInternals.H;
6767
if (dispatcher === null) {
6868
throw new Error(
6969
'react-cache: read and preload may only be called from within a ' +

0 commit comments

Comments
 (0)