Skip to content

Commit 4186952

Browse files
authored
Fixed incompatibility between react-debug-tools and useContext() (#14940)
* Refactor hook ordering check to use DEV-only data structure. This enables us to warn about more cases (e.g. useContext, useDebugValue) withou the need to add any overhead to production bundles.
1 parent 0b8efb2 commit 4186952

File tree

5 files changed

+600
-171
lines changed

5 files changed

+600
-171
lines changed

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

+38
Original file line numberDiff line numberDiff line change
@@ -427,4 +427,42 @@ describe('ReactHooksInspectionIntegration', () => {
427427
expect(setterCalls[0]).not.toBe(initial);
428428
expect(setterCalls[1]).toBe(initial);
429429
});
430+
431+
// This test case is based on an open source bug report:
432+
// facebookincubator/redux-react-hook/issues/34#issuecomment-466693787
433+
it('should properly advance the current hook for useContext', () => {
434+
const MyContext = React.createContext(1);
435+
436+
let incrementCount;
437+
438+
function Foo(props) {
439+
const context = React.useContext(MyContext);
440+
const [data, setData] = React.useState({count: context});
441+
442+
incrementCount = () => setData(({count}) => ({count: count + 1}));
443+
444+
return <div>count: {data.count}</div>;
445+
}
446+
447+
const renderer = ReactTestRenderer.create(<Foo />);
448+
expect(renderer.toJSON()).toEqual({
449+
type: 'div',
450+
props: {},
451+
children: ['count: ', '1'],
452+
});
453+
454+
act(incrementCount);
455+
expect(renderer.toJSON()).toEqual({
456+
type: 'div',
457+
props: {},
458+
children: ['count: ', '2'],
459+
});
460+
461+
const childFiber = renderer.root._currentFiber();
462+
const tree = ReactDebugTools.inspectHooksOfFiber(childFiber);
463+
expect(tree).toEqual([
464+
{name: 'Context', value: 1, subHooks: []},
465+
{name: 'State', value: {count: 2}, subHooks: []},
466+
]);
467+
});
430468
});

packages/react-reconciler/src/ReactFiber.js

+7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type {SideEffectTag} from 'shared/ReactSideEffectTags';
1515
import type {ExpirationTime} from './ReactFiberExpirationTime';
1616
import type {UpdateQueue} from './ReactUpdateQueue';
1717
import type {ContextDependencyList} from './ReactFiberNewContext';
18+
import type {HookType} from './ReactFiberHooks';
1819

1920
import invariant from 'shared/invariant';
2021
import warningWithoutStack from 'shared/warningWithoutStack';
@@ -204,6 +205,9 @@ export type Fiber = {|
204205
_debugSource?: Source | null,
205206
_debugOwner?: Fiber | null,
206207
_debugIsCurrentlyTiming?: boolean,
208+
209+
// Used to verify that the order of hooks does not change between renders.
210+
_debugHookTypes?: Array<HookType> | null,
207211
|};
208212

209213
let debugCounter;
@@ -285,6 +289,7 @@ function FiberNode(
285289
this._debugSource = null;
286290
this._debugOwner = null;
287291
this._debugIsCurrentlyTiming = false;
292+
this._debugHookTypes = null;
288293
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
289294
Object.preventExtensions(this);
290295
}
@@ -370,6 +375,7 @@ export function createWorkInProgress(
370375
workInProgress._debugID = current._debugID;
371376
workInProgress._debugSource = current._debugSource;
372377
workInProgress._debugOwner = current._debugOwner;
378+
workInProgress._debugHookTypes = current._debugHookTypes;
373379
}
374380

375381
workInProgress.alternate = current;
@@ -723,5 +729,6 @@ export function assignFiberPropertiesInDEV(
723729
target._debugSource = source._debugSource;
724730
target._debugOwner = source._debugOwner;
725731
target._debugIsCurrentlyTiming = source._debugIsCurrentlyTiming;
732+
target._debugHookTypes = source._debugHookTypes;
726733
return target;
727734
}

0 commit comments

Comments
 (0)