Repository: facebook/react
Pull request: [DevTools] Reorder moved filtered Fibers with backing DevToolsInstance

Mark them as reordered.

Changed files:
filename: packages/react-devtools-shared/src/__tests__/store-test.js
patch: @@ -17,8 +17,10 @@ describe('Store', () => {
   let act;
   let actAsync;
   let bridge;
+  let createDisplayNameFilter;
   let getRendererID;
   let legacyRender;
+  let previousComponentFilters;
   let store;
   let withErrorsOrWarningsIgnored;
 
@@ -29,6 +31,8 @@ describe('Store', () => {
     bridge = global.bridge;
     store = global.store;
 
+    previousComponentFilters = store.componentFilters;
+
     React = require('react');
     ReactDOM = require('react-dom');
     ReactDOMClient = require('react-dom/client');
@@ -38,9 +42,14 @@ describe('Store', () => {
     actAsync = utils.actAsync;
     getRendererID = utils.getRendererID;
     legacyRender = utils.legacyRender;
+    createDisplayNameFilter = utils.createDisplayNameFilter;
     withErrorsOrWarningsIgnored = utils.withErrorsOrWarningsIgnored;
   });
 
+  afterEach(() => {
+    store.componentFilters = previousComponentFilters;
+  });
+
   const {render, unmount, createContainer} = getVersionedRenderImplementation();
 
   // @reactVersion >= 18.0
@@ -129,6 +138,72 @@ describe('Store', () => {
     `);
   });
 
+  it('should handle reorder of filtered elements', async () => {
+    function IgnoreMePassthrough({children}) {
+      return children;
+    }
+    function PassThrough({children}) {
+      return children;
+    }
+
+    await actAsync(
+      async () =>
+        (store.componentFilters = [createDisplayNameFilter('^IgnoreMe', true)]),
+    );
+
+    await act(() => {
+      render(
+        <PassThrough key="e" name="e">
+          <IgnoreMePassthrough key="e1">
+            <PassThrough name="e-child-one">
+              <p>e1</p>
+            </PassThrough>
+          </IgnoreMePassthrough>
+          <IgnoreMePassthrough key="e2">
+            <PassThrough name="e-child-two">
+              <div>e2</div>
+            </PassThrough>
+          </IgnoreMePassthrough>
+        </PassThrough>,
+      );
+    });
+
+    expect(store).toMatchInlineSnapshot(`
+      [root]
+        ▾ <PassThrough key="e">
+          ▾ <PassThrough>
+              <p>
+          ▾ <PassThrough>
+              <div>
+    `);
+
+    await act(() => {
+      render(
+        <PassThrough key="e" name="e">
+          <IgnoreMePassthrough key="e2">
+            <PassThrough name="e-child-two">
+              <div>e2</div>
+            </PassThrough>
+          </IgnoreMePassthrough>
+          <IgnoreMePassthrough key="e1">
+            <PassThrough name="e-child-one">
+              <p>e1</p>
+            </PassThrough>
+          </IgnoreMePassthrough>
+        </PassThrough>,
+      );
+    });
+
+    expect(store).toMatchInlineSnapshot(`
+      [root]
+        ▾ <PassThrough key="e">
+          ▾ <PassThrough>
+              <div>
+          ▾ <PassThrough>
+              <p>
+    `);
+  });
+
   describe('StrictMode compliance', () => {
     it('should mark strict root elements as strict', async () => {
       const App = () => <Component />;

filename: packages/react-devtools-shared/src/backend/fiber/renderer.js
patch: @@ -3612,6 +3612,11 @@ export function attach(
             shouldResetChildren = true;
           }
         } else if (prevChild !== null && shouldFilterFiber(nextChild)) {
+          // The filtered instance could've reordered.
+          if (prevChild !== prevChildAtSameIndex) {
+            shouldResetChildren = true;
+          }
+
           // If this Fiber should be filtered, we need to still update its children.
           // This relies on an alternate since we don't have an Instance with the previous
           // child on it. Ideally, the reconciliation wouldn't need previous Fibers that