Skip to content

Commit a761b2f

Browse files
author
Brian Vaughn
committed
DevTools should properly report re-renders due to (use)context changes
1 parent e0aa5e2 commit a761b2f

File tree

2 files changed

+136
-1
lines changed

2 files changed

+136
-1
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
*/
9+
10+
import typeof ReactTestRenderer from 'react-test-renderer';
11+
12+
describe('Profiler change descriptions', () => {
13+
let React;
14+
let TestRenderer: ReactTestRenderer;
15+
let legacyRender;
16+
let store: Store;
17+
let utils;
18+
19+
beforeEach(() => {
20+
utils = require('./utils');
21+
utils.beforeEachProfiling();
22+
23+
legacyRender = utils.legacyRender;
24+
25+
store = global.store;
26+
store.collapseNodesByDefault = false;
27+
store.recordChangeDescriptions = true;
28+
29+
React = require('react');
30+
TestRenderer = utils.requireTestRenderer();
31+
});
32+
33+
it('should identify when context is the reason for an udpate', () => {
34+
const Context = React.createContext(0);
35+
36+
function Child() {
37+
const context = React.useContext(Context);
38+
return context;
39+
}
40+
41+
const MemoizedChild = React.memo(Child);
42+
const ForwardRefChild = React.forwardRef(function ForwardRef(props, ref) {
43+
return <Child />;
44+
});
45+
46+
let forceUpdate = null;
47+
48+
const MemoizedApp = React.memo(function App() {
49+
const [val, dispatch] = React.useReducer(x => x + 1, 0);
50+
51+
forceUpdate = dispatch;
52+
53+
return (
54+
<Context.Provider value={val}>
55+
<Child />
56+
<MemoizedChild />
57+
<ForwardRefChild />
58+
</Context.Provider>
59+
);
60+
});
61+
62+
const container = document.createElement('div');
63+
64+
utils.act(() => store.profilerStore.startProfiling());
65+
utils.act(() => legacyRender(<MemoizedApp />, container));
66+
utils.act(() => forceUpdate());
67+
utils.act(() => store.profilerStore.stopProfiling());
68+
69+
const rootID = store.roots[0];
70+
71+
let commitData = null;
72+
73+
function Validator({commitIndex}) {
74+
commitData = store.profilerStore.getCommitData(rootID, commitIndex);
75+
return null;
76+
}
77+
78+
utils.act(() => {
79+
TestRenderer.create(<Validator commitIndex={1} rootID={rootID} />);
80+
});
81+
82+
expect(store).toMatchInlineSnapshot(`
83+
[root]
84+
▾ <App> [Memo]
85+
▾ <Context.Provider>
86+
<Child>
87+
<Child> [Memo]
88+
▾ <ForwardRef> [ForwardRef]
89+
<Child>
90+
`);
91+
92+
const childID = store.getElementIDAtIndex(2);
93+
const meomizedChildID = store.getElementIDAtIndex(3);
94+
const childInsideForwardRefID = store.getElementIDAtIndex(5);
95+
96+
expect(commitData.changeDescriptions.get(childID)).toMatchInlineSnapshot(`
97+
Object {
98+
"context": true,
99+
"didHooksChange": false,
100+
"hooks": null,
101+
"isFirstMount": false,
102+
"props": Array [],
103+
"state": null,
104+
}
105+
`);
106+
expect(commitData.changeDescriptions.get(meomizedChildID))
107+
.toMatchInlineSnapshot(`
108+
Object {
109+
"context": true,
110+
"didHooksChange": false,
111+
"hooks": null,
112+
"isFirstMount": false,
113+
"props": Array [],
114+
"state": null,
115+
}
116+
`);
117+
expect(commitData.changeDescriptions.get(childInsideForwardRefID))
118+
.toMatchInlineSnapshot(`
119+
Object {
120+
"context": true,
121+
"didHooksChange": false,
122+
"hooks": null,
123+
"isFirstMount": false,
124+
"props": Array [],
125+
"state": null,
126+
}
127+
`);
128+
});
129+
});

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1253,8 +1253,10 @@ export function attach(
12531253

12541254
function updateContextsForFiber(fiber: Fiber) {
12551255
switch (getElementTypeForFiber(fiber)) {
1256-
case ElementTypeFunction:
12571256
case ElementTypeClass:
1257+
case ElementTypeForwardRef:
1258+
case ElementTypeFunction:
1259+
case ElementTypeMemo:
12581260
if (idToContextsMap !== null) {
12591261
const id = getFiberIDThrows(fiber);
12601262
const contexts = getContextsForFiber(fiber);
@@ -1292,7 +1294,9 @@ export function attach(
12921294
}
12931295
}
12941296
return [legacyContext, modernContext];
1297+
case ElementTypeForwardRef:
12951298
case ElementTypeFunction:
1299+
case ElementTypeMemo:
12961300
const dependencies = fiber.dependencies;
12971301
if (dependencies && dependencies.firstContext) {
12981302
modernContext = dependencies.firstContext;
@@ -1341,7 +1345,9 @@ export function attach(
13411345
}
13421346
}
13431347
break;
1348+
case ElementTypeForwardRef:
13441349
case ElementTypeFunction:
1350+
case ElementTypeMemo:
13451351
if (nextModernContext !== NO_CONTEXT) {
13461352
let prevContext = prevModernContext;
13471353
let nextContext = nextModernContext;

0 commit comments

Comments
 (0)