diff --git a/packages/react-devtools-shared/src/__tests__/optimisticKeyDevToolsIntegration.js b/packages/react-devtools-shared/src/__tests__/optimisticKeyDevToolsIntegration.js
new file mode 100644
index 000000000000..5a6fab25c3c4
--- /dev/null
+++ b/packages/react-devtools-shared/src/__tests__/optimisticKeyDevToolsIntegration.js
@@ -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 (
+
+
+
+
+
+
+
+ );
+ };
+
+ // @reactVersion >= 19.3
+ it('is included in the tree', async () => {
+ if (React.optimisticKey === undefined) {
+ return;
+ }
+
+ function Component() {
+ return null;
+ }
+
+ await actAsync(() => {
+ render();
+ });
+
+ expect(store).toMatchInlineSnapshot(`
+ [root]
+
+ `);
+ 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();
+ });
+ let renderer;
+ act(() => (renderer = TestRenderer.create()));
+
+ expect(state).toMatchInlineSnapshot(`
+ [root]
+
+ `);
+
+ act(() => dispatch({type: 'SET_SEARCH_TEXT', payload: 'optimistic'}));
+ act(() => renderer.update());
+
+ expect(state).toMatchInlineSnapshot(`
+ [root]
+
+ `);
+
+ act(() => dispatch({type: 'SET_SEARCH_TEXT', payload: 'react'}));
+ act(() => renderer.update());
+
+ expect(state).toMatchInlineSnapshot(`
+ [root]
+ →
+ `);
+ });
+});
diff --git a/packages/react-devtools-shared/src/backend/fiber/renderer.js b/packages/react-devtools-shared/src/backend/fiber/renderer.js
index 916d69823285..d58247f448f0 100644
--- a/packages/react-devtools-shared/src/backend/fiber/renderer.js
+++ b/packages/react-devtools-shared/src/backend/fiber/renderer.js
@@ -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 =
@@ -6179,7 +6184,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
@@ -6195,7 +6203,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:
@@ -7120,7 +7128,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,