[DevTools] Display React.optimisticKey in key positions (#35760)

This commit is contained in:
Sebastian "Sebbie" Silbermann
2026-02-11 00:35:36 +01:00
committed by GitHub
parent 57b79b0388
commit e49335e961
2 changed files with 148 additions and 4 deletions

View File

@@ -0,0 +1,131 @@
/**
* 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('Store React.optimisticKey', () => {
let act;
let actAsync;
let React;
let TestRenderer;
let bridge;
let store;
let BridgeContext;
let StoreContext;
let TreeContext;
let dispatch;
let state;
beforeAll(() => {
// JSDDOM doesn't implement getClientRects so we're just faking one for testing purposes
Element.prototype.getClientRects = function (this: Element) {
const textContent = this.textContent;
return [
new DOMRect(1, 2, textContent.length, textContent.split('\n').length),
];
};
});
beforeEach(() => {
global.IS_REACT_ACT_ENVIRONMENT = true;
store = global.store;
bridge = global.bridge;
React = require('react');
const utils = require('./utils');
act = utils.act;
actAsync = utils.actAsync;
TestRenderer = utils.requireTestRenderer();
BridgeContext =
require('react-devtools-shared/src/devtools/views/context').BridgeContext;
StoreContext =
require('react-devtools-shared/src/devtools/views/context').StoreContext;
TreeContext = require('react-devtools-shared/src/devtools/views/Components/TreeContext');
});
const {render} = getVersionedRenderImplementation();
const Capture = () => {
dispatch = React.useContext(TreeContext.TreeDispatcherContext);
state = React.useContext(TreeContext.TreeStateContext);
return null;
};
const Contexts = () => {
return (
<BridgeContext.Provider value={bridge}>
<StoreContext.Provider value={store}>
<TreeContext.TreeContextController>
<Capture />
</TreeContext.TreeContextController>
</StoreContext.Provider>
</BridgeContext.Provider>
);
};
// @reactVersion >= 19.3
it('is included in the tree', async () => {
if (React.optimisticKey === undefined) {
return;
}
function Component() {
return null;
}
await actAsync(() => {
render(<Component key={React.optimisticKey} />);
});
expect(store).toMatchInlineSnapshot(`
[root]
<Component key="React.optimisticKey">
`);
expect(store.getElementAtIndex(0)).toEqual(
expect.objectContaining({key: 'React.optimisticKey'}),
);
});
// @reactVersion >= 19.3
it('is searchable', async () => {
if (React.optimisticKey === undefined) {
return;
}
await actAsync(() => {
render(<React.Fragment key={React.optimisticKey} />);
});
let renderer;
act(() => (renderer = TestRenderer.create(<Contexts />)));
expect(state).toMatchInlineSnapshot(`
[root]
<Fragment key="React.optimisticKey">
`);
act(() => dispatch({type: 'SET_SEARCH_TEXT', payload: 'optimistic'}));
act(() => renderer.update(<Contexts />));
expect(state).toMatchInlineSnapshot(`
[root]
<Fragment key="React.optimisticKey">
`);
act(() => dispatch({type: 'SET_SEARCH_TEXT', payload: 'react'}));
act(() => renderer.update(<Contexts />));
expect(state).toMatchInlineSnapshot(`
[root]
→ <Fragment key="React.optimisticKey">
`);
});
});

View File

@@ -2606,7 +2606,12 @@ export function attach(
// This check is a guard to handle a React element that has been modified
// in such a way as to bypass the default stringification of the "key" property.
const keyString = key === null ? null : String(key);
const keyString =
key === null
? null
: key === REACT_OPTIMISTIC_KEY
? 'React.optimisticKey'
: String(key);
const keyStringID = getStringID(keyString);
const nameProp =
@@ -6180,7 +6185,10 @@ export function attach(
return {
displayName: getDisplayNameForFiber(fiber) || 'Anonymous',
id: instance.id,
key: fiber.key === REACT_OPTIMISTIC_KEY ? null : fiber.key,
key:
fiber.key === REACT_OPTIMISTIC_KEY
? 'React.optimisticKey'
: fiber.key,
env: null,
stack:
fiber._debugOwner == null || fiber._debugStack == null
@@ -6196,7 +6204,7 @@ export function attach(
key:
componentInfo.key == null ||
componentInfo.key === REACT_OPTIMISTIC_KEY
? null
? 'React.optimisticKey'
: componentInfo.key,
env: componentInfo.env == null ? null : componentInfo.env,
stack:
@@ -7123,7 +7131,12 @@ export function attach(
// Does the component have legacy context attached to it.
hasLegacyContext,
key: key != null && key !== REACT_OPTIMISTIC_KEY ? key : null,
key:
key != null
? key === REACT_OPTIMISTIC_KEY
? 'React.optimisticKey'
: key
: null,
type: elementType,