Repository: facebook/react
Pull request: [DevTools] Support secondary environment name when it changes

We currently support the Environment Name change within a Component. #29867

If this happens, we give it two HoCs. The problem with this is that we only show one followed by `+1` in the list.

Before:
<img width="529" alt="Screenshot 2024-08-28 at 6 50 31 PM" src="https://github.com/user-attachments/assets/c080be72-c254-4d4d-89b6-d1b7f9a9ada8">

After:
<img width="1101" alt="Screenshot 2024-08-28 at 7 16 21 PM" src="https://github.com/user-attachments/assets/04718674-164b-4255-9cf6-dec9198f12b7">

I could potentially instead badge this case as `A/B` in a single badge.

Changed files:
filename: packages/react-devtools-shared/src/backend/fiber/renderer.js
patch: @@ -7,7 +7,7 @@
  * @flow
  */
 
-import type {ReactComponentInfo} from 'shared/ReactTypes';
+import type {ReactComponentInfo, ReactDebugInfo} from 'shared/ReactTypes';
 
 import {
   ComponentFilterDisplayName,
@@ -2226,6 +2226,7 @@ export function attach(
   function recordVirtualMount(
     instance: VirtualInstance,
     parentInstance: DevToolsInstance | null,
+    secondaryEnv: null | string,
   ): void {
     const id = instance.id;
 
@@ -2239,6 +2240,9 @@ export function attach(
     let displayName = componentInfo.name || '';
     if (typeof env === 'string') {
       // We model environment as an HoC name for now.
+      if (secondaryEnv !== null) {
+        displayName = secondaryEnv + '(' + displayName + ')';
+      }
       displayName = env + '(' + displayName + ')';
     }
     const elementType = ElementTypeVirtual;
@@ -2444,6 +2448,25 @@ export function attach(
     pendingRealUnmountedIDs.push(id);
   }
 
+  function getSecondaryEnvironmentName(
+    debugInfo: ?ReactDebugInfo,
+    index: number,
+  ): null | string {
+    if (debugInfo != null) {
+      const componentInfo: ReactComponentInfo = (debugInfo[index]: any);
+      for (let i = index + 1; i < debugInfo.length; i++) {
+        const debugEntry = debugInfo[i];
+        if (typeof debugEntry.env === 'string') {
+          // If the next environment is different then this component was the boundary
+          // and it changed before entering the next component. So we assign this
+          // component a secondary environment.
+          return componentInfo.env !== debugEntry.env ? debugEntry.env : null;
+        }
+      }
+    }
+    return null;
+  }
+
   function mountVirtualChildrenRecursively(
     firstChild: Fiber,
     lastChild: null | Fiber, // non-inclusive
@@ -2464,6 +2487,7 @@ export function attach(
             // Not a Component. Some other Debug Info.
             continue;
           }
+          // Scan up until the next Component to see if this component changed environment.
           const componentInfo: ReactComponentInfo = (debugEntry: any);
           if (shouldFilterVirtual(componentInfo)) {
             // Skip.
@@ -2487,7 +2511,15 @@ export function attach(
                 );
               }
               previousVirtualInstance = createVirtualInstance(componentInfo);
-              recordVirtualMount(previousVirtualInstance, reconcilingParent);
+              const secondaryEnv = getSecondaryEnvironmentName(
+                fiber._debugInfo,
+                i,
+              );
+              recordVirtualMount(
+                previousVirtualInstance,
+                reconcilingParent,
+                secondaryEnv,
+              );
               insertChild(previousVirtualInstance);
               previousVirtualInstanceFirstFiber = fiber;
             }
@@ -2951,7 +2983,15 @@ export function attach(
               } else {
                 // Otherwise we create a new instance.
                 const newVirtualInstance = createVirtualInstance(componentInfo);
-                recordVirtualMount(newVirtualInstance, reconcilingParent);
+                const secondaryEnv = getSecondaryEnvironmentName(
+                  nextChild._debugInfo,
+                  i,
+                );
+                recordVirtualMount(
+                  newVirtualInstance,
+                  reconcilingParent,
+                  secondaryEnv,
+                );
                 insertChild(newVirtualInstance);
                 previousVirtualInstance = newVirtualInstance;
                 previousVirtualInstanceWasMount = true;