-
Notifications
You must be signed in to change notification settings - Fork 47k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DevTools should properly report re-renders due to (use)context changes (
#22746) Note that this only fixes things for newer versions of React (e.g. 18 alpha). Older versions will remain broken because there's not a good way to read the most recent context value for a location in the tree after render has completed. This is because React maintains a stack of context values during render, but by the time DevTools is called– render has finished and the stack is empty.
- Loading branch information
Brian Vaughn
authored
Nov 11, 2021
1 parent
a44a7a2
commit 56efc65
Showing
2 changed files
with
162 additions
and
1 deletion.
There are no files selected for viewing
151 changes: 151 additions & 0 deletions
151
packages/react-devtools-shared/src/__tests__/profilerChangeDescriptions-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
/** | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* @flow | ||
*/ | ||
|
||
describe('Profiler change descriptions', () => { | ||
let React; | ||
let legacyRender; | ||
let store: Store; | ||
let utils; | ||
|
||
beforeEach(() => { | ||
utils = require('./utils'); | ||
utils.beforeEachProfiling(); | ||
|
||
legacyRender = utils.legacyRender; | ||
|
||
store = global.store; | ||
store.collapseNodesByDefault = false; | ||
store.recordChangeDescriptions = true; | ||
|
||
React = require('react'); | ||
}); | ||
|
||
it('should identify useContext as the cause for a re-render', () => { | ||
const Context = React.createContext(0); | ||
|
||
function Child() { | ||
const context = React.useContext(Context); | ||
return context; | ||
} | ||
|
||
function areEqual() { | ||
return true; | ||
} | ||
|
||
const MemoizedChild = React.memo(Child, areEqual); | ||
const ForwardRefChild = React.forwardRef(function RefForwardingComponent( | ||
props, | ||
ref, | ||
) { | ||
return <Child />; | ||
}); | ||
|
||
let forceUpdate = null; | ||
|
||
const App = function App() { | ||
const [val, dispatch] = React.useReducer(x => x + 1, 0); | ||
|
||
forceUpdate = dispatch; | ||
|
||
return ( | ||
<Context.Provider value={val}> | ||
<Child /> | ||
<MemoizedChild /> | ||
<ForwardRefChild /> | ||
</Context.Provider> | ||
); | ||
}; | ||
|
||
const container = document.createElement('div'); | ||
|
||
utils.act(() => store.profilerStore.startProfiling()); | ||
utils.act(() => legacyRender(<App />, container)); | ||
utils.act(() => forceUpdate()); | ||
utils.act(() => store.profilerStore.stopProfiling()); | ||
|
||
const rootID = store.roots[0]; | ||
const commitData = store.profilerStore.getCommitData(rootID, 1); | ||
|
||
expect(store).toMatchInlineSnapshot(` | ||
[root] | ||
▾ <App> | ||
▾ <Context.Provider> | ||
<Child> | ||
▾ <Child> [Memo] | ||
<Child> | ||
▾ <RefForwardingComponent> [ForwardRef] | ||
<Child> | ||
`); | ||
|
||
let element = store.getElementAtIndex(2); | ||
expect(element.displayName).toBe('Child'); | ||
expect(element.hocDisplayNames).toBeNull(); | ||
expect(commitData.changeDescriptions.get(element.id)) | ||
.toMatchInlineSnapshot(` | ||
Object { | ||
"context": true, | ||
"didHooksChange": false, | ||
"hooks": null, | ||
"isFirstMount": false, | ||
"props": Array [], | ||
"state": null, | ||
} | ||
`); | ||
|
||
element = store.getElementAtIndex(3); | ||
expect(element.displayName).toBe('Child'); | ||
expect(element.hocDisplayNames).toEqual(['Memo']); | ||
expect(commitData.changeDescriptions.get(element.id)).toBeUndefined(); | ||
|
||
element = store.getElementAtIndex(4); | ||
expect(element.displayName).toBe('Child'); | ||
expect(element.hocDisplayNames).toBeNull(); | ||
expect(commitData.changeDescriptions.get(element.id)) | ||
.toMatchInlineSnapshot(` | ||
Object { | ||
"context": true, | ||
"didHooksChange": false, | ||
"hooks": null, | ||
"isFirstMount": false, | ||
"props": Array [], | ||
"state": null, | ||
} | ||
`); | ||
|
||
element = store.getElementAtIndex(5); | ||
expect(element.displayName).toBe('RefForwardingComponent'); | ||
expect(element.hocDisplayNames).toEqual(['ForwardRef']); | ||
expect(commitData.changeDescriptions.get(element.id)) | ||
.toMatchInlineSnapshot(` | ||
Object { | ||
"context": null, | ||
"didHooksChange": false, | ||
"hooks": null, | ||
"isFirstMount": false, | ||
"props": Array [], | ||
"state": null, | ||
} | ||
`); | ||
|
||
element = store.getElementAtIndex(6); | ||
expect(element.displayName).toBe('Child'); | ||
expect(element.hocDisplayNames).toBeNull(); | ||
expect(commitData.changeDescriptions.get(element.id)) | ||
.toMatchInlineSnapshot(` | ||
Object { | ||
"context": true, | ||
"didHooksChange": false, | ||
"hooks": null, | ||
"isFirstMount": false, | ||
"props": Array [], | ||
"state": null, | ||
} | ||
`); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters