@samitouri / QOS-React / commits / f290138d32

react-debug-tools accepts currentDispatcher ref as param (#14556)

* react-debug-tools accepts currentDispatcher ref as param * ReactDebugHooks injected dispatcher ref is optional

Brian Vaughn committed Jan 10, 2019 at 12:56 UTC f290138d329aa7aa635d88868705b23372e9f004
3 files changed +100 -10
packages/react-debug-tools/src/ReactDebugHooks.js
+32 -10
@@ -20,7 +20,7 @@ import {
20 ForwardRef,
21 } from 'shared/ReactWorkTags';
22
23 -const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
23 +type CurrentDispatcherRef = typeof ReactSharedInternals.ReactCurrentDispatcher;
24
25 // Used to track hooks called during a render
26
@@ -408,10 +408,17 @@ function buildTree(rootStack, readHookLog): HooksTree {
408 export function inspectHooks<Props>(
409 renderFunction: Props => React$Node,
410 props: Props,
411 + currentDispatcher: ?CurrentDispatcherRef,
412 ): 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;
420 let readHookLog;
414 - ReactCurrentDispatcher.current = Dispatcher;
421 + currentDispatcher.current = Dispatcher;
422 let ancestorStackError;
423 try {
424 ancestorStackError = new Error();
@@ -419,7 +426,7 @@ export function inspectHooks<Props>(
426 } finally {
427 readHookLog = hookLog;
428 hookLog = [];
422 - ReactCurrentDispatcher.current = previousDispatcher;
429 + currentDispatcher.current = previousDispatcher;
430 }
431 let rootStack = ErrorStackParser.parse(ancestorStackError);
432 return buildTree(rootStack, readHookLog);
@@ -450,10 +457,11 @@ function inspectHooksOfForwardRef<Props, Ref>(
457 renderFunction: (Props, Ref) => React$Node,
458 props: Props,
459 ref: Ref,
460 + currentDispatcher: CurrentDispatcherRef,
461 ): HooksTree {
454 - let previousDispatcher = ReactCurrentDispatcher.current;
462 + let previousDispatcher = currentDispatcher.current;
463 let readHookLog;
456 - ReactCurrentDispatcher.current = Dispatcher;
464 + currentDispatcher.current = Dispatcher;
465 let ancestorStackError;
466 try {
467 ancestorStackError = new Error();
@@ -461,7 +469,7 @@ function inspectHooksOfForwardRef<Props, Ref>(
469 } finally {
470 readHookLog = hookLog;
471 hookLog = [];
464 - ReactCurrentDispatcher.current = previousDispatcher;
472 + currentDispatcher.current = previousDispatcher;
473 }
474 let rootStack = ErrorStackParser.parse(ancestorStackError);
475 return buildTree(rootStack, readHookLog);
@@ -482,7 +490,16 @@ function resolveDefaultProps(Component, baseProps) {
490 return baseProps;
491 }
492
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 +
503 if (
504 fiber.tag !== FunctionComponent &&
505 fiber.tag !== SimpleMemoComponent &&
@@ -506,9 +523,14 @@ export function inspectHooksOfFiber(fiber: Fiber) {
523 try {
524 setupContexts(contextMap, fiber);
525 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 + );
532 }
511 - return inspectHooks(type, props);
533 + return inspectHooks(type, props, currentDispatcher);
534 } finally {
535 currentHook = null;
536 restoreContexts(contextMap);
packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js
+33
@@ -216,4 +216,37 @@ describe('ReactHooksInspection', () => {
216 },
217 ]);
218 });
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 + });
252 });
packages/react-debug-tools/src/__tests__/ReactHooksInspectionIntegration-test.internal.js
+35
@@ -241,4 +241,39 @@ describe('ReactHooksInspectionIntergration', () => {
241 let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
242 expect(tree).toEqual([{name: 'State', value: 'def', subHooks: []}]);
243 });
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 + });
279 });