Files
react/packages/react-devtools-shared/src/__tests__/profilerChangeDescriptions-test.js
Sebastian Markbåge 6292398241 [DevTools] Simplify Context Change Tracking in Profiler (#30896)
When Context change tracking was added to support modern Context it
relied on the "memoizedValue" to read the current value. This only works
in React 18+ when it was added to support Lazy Context Propagation.
However, the backend stored the old value the same way it used to work
for legacy Context in a global map. This was unnecessary since we *also*
have the old value on the previous Fiber.

This removes all the costly tracking of previous values for every Fiber
that uses Contexts slowing down profiling. Instead, we just compare the
Contexts from

The downside is that this no longer supports detecting changes due to
legacy Context because it doesn't have a similar "previous" value.
However, legacy Context has long been deprecated and is completely
removed in 19. So I don't think it's worth supporting since you have to
be on an old version *and* actually use legacy Context *and* trying to
profile something that updates it. Which btw, updating legacy contexts
only worked at all from 16 something when we made updates work. So it
was unusual even in the slight gap where you could and before you had
migrated to modern Context introduced in 16.3.
2024-09-06 21:58:20 -04:00

151 lines
3.8 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import {getVersionedRenderImplementation} from './utils';
describe('Profiler change descriptions', () => {
let React;
let store;
let utils;
beforeEach(() => {
utils = require('./utils');
utils.beforeEachProfiling();
store = global.store;
store.collapseNodesByDefault = false;
store.recordChangeDescriptions = true;
React = require('react');
});
const {render} = getVersionedRenderImplementation();
// @reactVersion >=18.0
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>
);
};
utils.act(() => store.profilerStore.startProfiling());
utils.act(() => render(<App />));
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(`
{
"context": true,
"didHooksChange": false,
"hooks": null,
"isFirstMount": false,
"props": [],
"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(`
{
"context": true,
"didHooksChange": false,
"hooks": null,
"isFirstMount": false,
"props": [],
"state": null,
}
`);
element = store.getElementAtIndex(5);
expect(element.displayName).toBe('RefForwardingComponent');
expect(element.hocDisplayNames).toEqual(['ForwardRef']);
expect(commitData.changeDescriptions.get(element.id))
.toMatchInlineSnapshot(`
{
"context": false,
"didHooksChange": false,
"hooks": null,
"isFirstMount": false,
"props": [],
"state": null,
}
`);
element = store.getElementAtIndex(6);
expect(element.displayName).toBe('Child');
expect(element.hocDisplayNames).toBeNull();
expect(commitData.changeDescriptions.get(element.id))
.toMatchInlineSnapshot(`
{
"context": true,
"didHooksChange": false,
"hooks": null,
"isFirstMount": false,
"props": [],
"state": null,
}
`);
});
});