Skip to content

Commit

Permalink
ReactDebugHooks injected dispatcher ref is optional
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Vaughn committed Jan 10, 2019
1 parent 6f332dc commit 721f16c
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 51 deletions.
22 changes: 17 additions & 5 deletions packages/react-debug-tools/src/ReactDebugHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,10 +406,16 @@ function buildTree(rootStack, readHookLog): HooksTree {
}

export function inspectHooks<Props>(
currentDispatcher: CurrentDispatcherRef,
renderFunction: Props => React$Node,
props: Props,
currentDispatcher: ?CurrentDispatcherRef,
): HooksTree {
// DevTools will pass the current renderer's injected dispatcher.
// Other apps might compile debug hooks as part of their app though.
if (currentDispatcher == null) {
currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
}

let previousDispatcher = currentDispatcher.current;
let readHookLog;
currentDispatcher.current = Dispatcher;
Expand Down Expand Up @@ -448,10 +454,10 @@ function restoreContexts(contextMap: Map<ReactContext<any>, any>) {
}

function inspectHooksOfForwardRef<Props, Ref>(
currentDispatcher: CurrentDispatcherRef,
renderFunction: (Props, Ref) => React$Node,
props: Props,
ref: Ref,
currentDispatcher: CurrentDispatcherRef,
): HooksTree {
let previousDispatcher = currentDispatcher.current;
let readHookLog;
Expand Down Expand Up @@ -485,9 +491,15 @@ function resolveDefaultProps(Component, baseProps) {
}

export function inspectHooksOfFiber(
currentDispatcher: CurrentDispatcherRef,
fiber: Fiber,
currentDispatcher: ?CurrentDispatcherRef,
) {
// DevTools will pass the current renderer's injected dispatcher.
// Other apps might compile debug hooks as part of their app though.
if (currentDispatcher == null) {
currentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
}

if (
fiber.tag !== FunctionComponent &&
fiber.tag !== SimpleMemoComponent &&
Expand All @@ -512,13 +524,13 @@ export function inspectHooksOfFiber(
setupContexts(contextMap, fiber);
if (fiber.tag === ForwardRef) {
return inspectHooksOfForwardRef(
currentDispatcher,
type.render,
props,
fiber.ref,
currentDispatcher,
);
}
return inspectHooks(currentDispatcher, type, props);
return inspectHooks(type, props, currentDispatcher);
} finally {
currentHook = null;
restoreContexts(contextMap);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

let React;
let ReactDebugTools;
let currentDispatcher;

describe('ReactHooksInspection', () => {
beforeEach(() => {
Expand All @@ -22,18 +21,14 @@ describe('ReactHooksInspection', () => {
ReactFeatureFlags.enableHooks = true;
React = require('react');
ReactDebugTools = require('react-debug-tools');

currentDispatcher =
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
.ReactCurrentDispatcher;
});

it('should inspect a simple useState hook', () => {
function Foo(props) {
let [state] = React.useState('hello world');
return <div>{state}</div>;
}
let tree = ReactDebugTools.inspectHooks(currentDispatcher, Foo, {});
let tree = ReactDebugTools.inspectHooks(Foo, {});
expect(tree).toEqual([
{
name: 'State',
Expand All @@ -52,7 +47,7 @@ describe('ReactHooksInspection', () => {
let value = useCustom('hello world');
return <div>{value}</div>;
}
let tree = ReactDebugTools.inspectHooks(currentDispatcher, Foo, {});
let tree = ReactDebugTools.inspectHooks(Foo, {});
expect(tree).toEqual([
{
name: 'Custom',
Expand Down Expand Up @@ -84,7 +79,7 @@ describe('ReactHooksInspection', () => {
</div>
);
}
let tree = ReactDebugTools.inspectHooks(currentDispatcher, Foo, {});
let tree = ReactDebugTools.inspectHooks(Foo, {});
expect(tree).toEqual([
{
name: 'Custom',
Expand Down Expand Up @@ -147,7 +142,7 @@ describe('ReactHooksInspection', () => {
</div>
);
}
let tree = ReactDebugTools.inspectHooks(currentDispatcher, Foo, {});
let tree = ReactDebugTools.inspectHooks(Foo, {});
expect(tree).toEqual([
{
name: 'Bar',
Expand Down Expand Up @@ -212,7 +207,7 @@ describe('ReactHooksInspection', () => {
let value = React.useContext(MyContext);
return <div>{value}</div>;
}
let tree = ReactDebugTools.inspectHooks(currentDispatcher, Foo, {});
let tree = ReactDebugTools.inspectHooks(Foo, {});
expect(tree).toEqual([
{
name: 'Context',
Expand All @@ -221,4 +216,37 @@ describe('ReactHooksInspection', () => {
},
]);
});

it('should support an injected dispatcher', () => {
function Foo(props) {
let [state] = React.useState('hello world');
return <div>{state}</div>;
}

let initial = {};
let current = initial;
let getterCalls = 0;
let setterCalls = [];
let FakeDispatcherRef = {
get current() {
getterCalls++;
return current;
},
set current(value) {
setterCalls.push(value);
current = value;
},
};

expect(() => {
ReactDebugTools.inspectHooks(Foo, {}, FakeDispatcherRef);
}).toThrow(
'Hooks can only be called inside the body of a function component.',
);

expect(getterCalls).toBe(1);
expect(setterCalls).toHaveLength(2);
expect(setterCalls[0]).not.toBe(initial);
expect(setterCalls[1]).toBe(initial);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
let React;
let ReactTestRenderer;
let ReactDebugTools;
let currentDispatcher;

describe('ReactHooksInspectionIntergration', () => {
beforeEach(() => {
Expand All @@ -24,10 +23,6 @@ describe('ReactHooksInspectionIntergration', () => {
React = require('react');
ReactTestRenderer = require('react-test-renderer');
ReactDebugTools = require('react-debug-tools');

currentDispatcher =
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
.ReactCurrentDispatcher;
});

it('should inspect the current state of useState hooks', () => {
Expand All @@ -44,10 +39,7 @@ describe('ReactHooksInspectionIntergration', () => {
let renderer = ReactTestRenderer.create(<Foo prop="prop" />);

let childFiber = renderer.root.findByType(Foo)._currentFiber();
let tree = ReactDebugTools.inspectHooksOfFiber(
currentDispatcher,
childFiber,
);
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
expect(tree).toEqual([
{name: 'State', value: 'hello', subHooks: []},
{name: 'State', value: 'world', subHooks: []},
Expand All @@ -61,7 +53,7 @@ describe('ReactHooksInspectionIntergration', () => {
setStateA('Hi');

childFiber = renderer.root.findByType(Foo)._currentFiber();
tree = ReactDebugTools.inspectHooksOfFiber(currentDispatcher, childFiber);
tree = ReactDebugTools.inspectHooksOfFiber(childFiber);

expect(tree).toEqual([
{name: 'State', value: 'Hi', subHooks: []},
Expand All @@ -71,7 +63,7 @@ describe('ReactHooksInspectionIntergration', () => {
setStateB('world!');

childFiber = renderer.root.findByType(Foo)._currentFiber();
tree = ReactDebugTools.inspectHooksOfFiber(currentDispatcher, childFiber);
tree = ReactDebugTools.inspectHooksOfFiber(childFiber);

expect(tree).toEqual([
{name: 'State', value: 'Hi', subHooks: []},
Expand Down Expand Up @@ -119,10 +111,7 @@ describe('ReactHooksInspectionIntergration', () => {

let {onClick: updateStates} = renderer.root.findByType('div').props;

let tree = ReactDebugTools.inspectHooksOfFiber(
currentDispatcher,
childFiber,
);
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
expect(tree).toEqual([
{name: 'State', value: 'a', subHooks: []},
{name: 'Reducer', value: 'b', subHooks: []},
Expand All @@ -137,7 +126,7 @@ describe('ReactHooksInspectionIntergration', () => {
updateStates();

childFiber = renderer.root.findByType(Foo)._currentFiber();
tree = ReactDebugTools.inspectHooksOfFiber(currentDispatcher, childFiber);
tree = ReactDebugTools.inspectHooksOfFiber(childFiber);

expect(tree).toEqual([
{name: 'State', value: 'A', subHooks: []},
Expand All @@ -163,10 +152,7 @@ describe('ReactHooksInspectionIntergration', () => {
</MyContext.Provider>,
);
let childFiber = renderer.root.findByType(Foo)._currentFiber();
let tree = ReactDebugTools.inspectHooksOfFiber(
currentDispatcher,
childFiber,
);
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
expect(tree).toEqual([
{
name: 'Context',
Expand All @@ -186,10 +172,7 @@ describe('ReactHooksInspectionIntergration', () => {
let renderer = ReactTestRenderer.create(<Foo ref={ref} />);

let childFiber = renderer.root.findByType(Foo)._currentFiber();
let tree = ReactDebugTools.inspectHooksOfFiber(
currentDispatcher,
childFiber,
);
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
expect(tree).toEqual([
{name: 'ImperativeHandle', value: obj, subHooks: []},
]);
Expand All @@ -204,10 +187,7 @@ describe('ReactHooksInspectionIntergration', () => {
let renderer = ReactTestRenderer.create(<Foo />);
// TODO: Test renderer findByType is broken for memo. Have to search for the inner.
let childFiber = renderer.root.findByType(InnerFoo)._currentFiber();
let tree = ReactDebugTools.inspectHooksOfFiber(
currentDispatcher,
childFiber,
);
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
expect(tree).toEqual([{name: 'State', value: 'hello', subHooks: []}]);
});

Expand All @@ -222,10 +202,7 @@ describe('ReactHooksInspectionIntergration', () => {
}
let renderer = ReactTestRenderer.create(<Foo />);
let childFiber = renderer.root.findByType(Foo)._currentFiber();
let tree = ReactDebugTools.inspectHooksOfFiber(
currentDispatcher,
childFiber,
);
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
expect(tree).toEqual([
{
name: 'Custom',
Expand Down Expand Up @@ -261,10 +238,42 @@ describe('ReactHooksInspectionIntergration', () => {
await LazyFoo;

let childFiber = renderer.root._currentFiber();
let tree = ReactDebugTools.inspectHooksOfFiber(
currentDispatcher,
childFiber,
);
let tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
expect(tree).toEqual([{name: 'State', value: 'def', subHooks: []}]);
});

it('should support an injected dispatcher', () => {
function Foo(props) {
let [state] = React.useState('hello world');
return <div>{state}</div>;
}

let initial = {};
let current = initial;
let getterCalls = 0;
let setterCalls = [];
let FakeDispatcherRef = {
get current() {
getterCalls++;
return current;
},
set current(value) {
setterCalls.push(value);
current = value;
},
};

let renderer = ReactTestRenderer.create(<Foo />);
let childFiber = renderer.root._currentFiber();
expect(() => {
ReactDebugTools.inspectHooksOfFiber(childFiber, FakeDispatcherRef);
}).toThrow(
'Hooks can only be called inside the body of a function component.',
);

expect(getterCalls).toBe(1);
expect(setterCalls).toHaveLength(2);
expect(setterCalls[0]).not.toBe(initial);
expect(setterCalls[1]).toBe(initial);
});
});

0 comments on commit 721f16c

Please sign in to comment.