Skip to content

Commit f290138

Browse files
authored
react-debug-tools accepts currentDispatcher ref as param (#14556)
* react-debug-tools accepts currentDispatcher ref as param * ReactDebugHooks injected dispatcher ref is optional
1 parent b4ad8e9 commit f290138

File tree

3 files changed

+100
-10
lines changed

3 files changed

+100
-10
lines changed

Diff for: packages/react-debug-tools/src/ReactDebugHooks.js

+32-10
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
ForwardRef,
2121
} from 'shared/ReactWorkTags';
2222

23-
const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
23+
type CurrentDispatcherRef = typeof ReactSharedInternals.ReactCurrentDispatcher;
2424

2525
// Used to track hooks called during a render
2626

@@ -408,18 +408,25 @@ function buildTree(rootStack, readHookLog): HooksTree {
408408
export function inspectHooks<Props>(
409409
renderFunction: Props => React$Node,
410410
props: Props,
411+
currentDispatcher: ?CurrentDispatcherRef,
411412
): HooksTree {
412-
let previousDispatcher = ReactCurrentDispatcher.current;
413+
// DevTools will pass the current renderer's injected dispatcher.
414+
// Other apps might compile debug hooks as part of their app though.
415+
if (currentDispatcher == null) {
416+
currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
417+
}
418+
419+
let previousDispatcher = currentDispatcher.current;
413420
let readHookLog;
414-
ReactCurrentDispatcher.current = Dispatcher;
421+
currentDispatcher.current = Dispatcher;
415422
let ancestorStackError;
416423
try {
417424
ancestorStackError = new Error();
418425
renderFunction(props);
419426
} finally {
420427
readHookLog = hookLog;
421428
hookLog = [];
422-
ReactCurrentDispatcher.current = previousDispatcher;
429+
currentDispatcher.current = previousDispatcher;
423430
}
424431
let rootStack = ErrorStackParser.parse(ancestorStackError);
425432
return buildTree(rootStack, readHookLog);
@@ -450,18 +457,19 @@ function inspectHooksOfForwardRef<Props, Ref>(
450457
renderFunction: (Props, Ref) => React$Node,
451458
props: Props,
452459
ref: Ref,
460+
currentDispatcher: CurrentDispatcherRef,
453461
): HooksTree {
454-
let previousDispatcher = ReactCurrentDispatcher.current;
462+
let previousDispatcher = currentDispatcher.current;
455463
let readHookLog;
456-
ReactCurrentDispatcher.current = Dispatcher;
464+
currentDispatcher.current = Dispatcher;
457465
let ancestorStackError;
458466
try {
459467
ancestorStackError = new Error();
460468
renderFunction(props, ref);
461469
} finally {
462470
readHookLog = hookLog;
463471
hookLog = [];
464-
ReactCurrentDispatcher.current = previousDispatcher;
472+
currentDispatcher.current = previousDispatcher;
465473
}
466474
let rootStack = ErrorStackParser.parse(ancestorStackError);
467475
return buildTree(rootStack, readHookLog);
@@ -482,7 +490,16 @@ function resolveDefaultProps(Component, baseProps) {
482490
return baseProps;
483491
}
484492

485-
export function inspectHooksOfFiber(fiber: Fiber) {
493+
export function inspectHooksOfFiber(
494+
fiber: Fiber,
495+
currentDispatcher: ?CurrentDispatcherRef,
496+
) {
497+
// DevTools will pass the current renderer's injected dispatcher.
498+
// Other apps might compile debug hooks as part of their app though.
499+
if (currentDispatcher == null) {
500+
currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
501+
}
502+
486503
if (
487504
fiber.tag !== FunctionComponent &&
488505
fiber.tag !== SimpleMemoComponent &&
@@ -506,9 +523,14 @@ export function inspectHooksOfFiber(fiber: Fiber) {
506523
try {
507524
setupContexts(contextMap, fiber);
508525
if (fiber.tag === ForwardRef) {
509-
return inspectHooksOfForwardRef(type.render, props, fiber.ref);
526+
return inspectHooksOfForwardRef(
527+
type.render,
528+
props,
529+
fiber.ref,
530+
currentDispatcher,
531+
);
510532
}
511-
return inspectHooks(type, props);
533+
return inspectHooks(type, props, currentDispatcher);
512534
} finally {
513535
currentHook = null;
514536
restoreContexts(contextMap);

Diff for: packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js

+33
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,37 @@ describe('ReactHooksInspection', () => {
216216
},
217217
]);
218218
});
219+
220+
it('should support an injected dispatcher', () => {
221+
function Foo(props) {
222+
let [state] = React.useState('hello world');
223+
return <div>{state}</div>;
224+
}
225+
226+
let initial = {};
227+
let current = initial;
228+
let getterCalls = 0;
229+
let setterCalls = [];
230+
let FakeDispatcherRef = {
231+
get current() {
232+
getterCalls++;
233+
return current;
234+
},
235+
set current(value) {
236+
setterCalls.push(value);
237+
current = value;
238+
},
239+
};
240+
241+
expect(() => {
242+
ReactDebugTools.inspectHooks(Foo, {}, FakeDispatcherRef);
243+
}).toThrow(
244+
'Hooks can only be called inside the body of a function component.',
245+
);
246+
247+
expect(getterCalls).toBe(1);
248+
expect(setterCalls).toHaveLength(2);
249+
expect(setterCalls[0]).not.toBe(initial);
250+
expect(setterCalls[1]).toBe(initial);
251+
});
219252
});

Diff for: packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.internal.js

+35
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,39 @@ describe('ReactHooksInspectionIntergration', () => {
241241
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
242242
expect(tree).toEqual([{name: 'State', value: 'def', subHooks: []}]);
243243
});
244+
245+
it('should support an injected dispatcher', () => {
246+
function Foo(props) {
247+
let [state] = React.useState('hello world');
248+
return <div>{state}</div>;
249+
}
250+
251+
let initial = {};
252+
let current = initial;
253+
let getterCalls = 0;
254+
let setterCalls = [];
255+
let FakeDispatcherRef = {
256+
get current() {
257+
getterCalls++;
258+
return current;
259+
},
260+
set current(value) {
261+
setterCalls.push(value);
262+
current = value;
263+
},
264+
};
265+
266+
let renderer = ReactTestRenderer.create(<Foo />);
267+
let childFiber = renderer.root._currentFiber();
268+
expect(() => {
269+
ReactDebugTools.inspectHooksOfFiber(childFiber, FakeDispatcherRef);
270+
}).toThrow(
271+
'Hooks can only be called inside the body of a function component.',
272+
);
273+
274+
expect(getterCalls).toBe(1);
275+
expect(setterCalls).toHaveLength(2);
276+
expect(setterCalls[0]).not.toBe(initial);
277+
expect(setterCalls[1]).toBe(initial);
278+
});
244279
});

0 commit comments

Comments
 (0)