[DevTools] Reorder moved filtered Fibers with backing DevToolsInstance (#34104)
Instead, we just continue to collect the unfiltered children. --------- Co-authored-by: Sebastian Sebbie Silbermann <sebastian.silbermann@vercel.com>
Sebastian Markbåge committed
Aug 5, 2025 at 12:39 UTC
99fd4f2ac1fc9bd6b7aeb57afb2bcb1599ad1165
2 files changed
+80
packages/react-devtools-shared/src/__tests__/store-test.js
+75
@@ -17,8 +17,10 @@ describe('Store', () => {
17
let act;
18
let actAsync;
19
let bridge;
20
+ let createDisplayNameFilter;
21
let getRendererID;
22
let legacyRender;
23
+ let previousComponentFilters;
24
let store;
25
let withErrorsOrWarningsIgnored;
26
@@ -29,6 +31,8 @@ describe('Store', () => {
31
bridge = global.bridge;
32
store = global.store;
33
34
+ previousComponentFilters = store.componentFilters;
35
+
36
React = require('react');
37
ReactDOM = require('react-dom');
38
ReactDOMClient = require('react-dom/client');
@@ -38,9 +42,14 @@ describe('Store', () => {
42
actAsync = utils.actAsync;
43
getRendererID = utils.getRendererID;
44
legacyRender = utils.legacyRender;
45
+ createDisplayNameFilter = utils.createDisplayNameFilter;
46
withErrorsOrWarningsIgnored = utils.withErrorsOrWarningsIgnored;
47
});
48
49
+ afterEach(() => {
50
+ store.componentFilters = previousComponentFilters;
51
+ });
52
+
53
const {render, unmount, createContainer} = getVersionedRenderImplementation();
54
55
// @reactVersion >= 18.0
@@ -129,6 +138,72 @@ describe('Store', () => {
138
`);
139
});
140
141
+ it('should handle reorder of filtered elements', async () => {
142
+ function IgnoreMePassthrough({children}) {
143
+ return children;
144
+ }
145
+ function PassThrough({children}) {
146
+ return children;
147
+ }
148
+
149
+ await actAsync(
150
+ async () =>
151
+ (store.componentFilters = [createDisplayNameFilter('^IgnoreMe', true)]),
152
+ );
153
+
154
+ await act(() => {
155
+ render(
156
+ <PassThrough key="e" name="e">
157
+ <IgnoreMePassthrough key="e1">
158
+ <PassThrough name="e-child-one">
159
+ <p>e1</p>
160
+ </PassThrough>
161
+ </IgnoreMePassthrough>
162
+ <IgnoreMePassthrough key="e2">
163
+ <PassThrough name="e-child-two">
164
+ <div>e2</div>
165
+ </PassThrough>
166
+ </IgnoreMePassthrough>
167
+ </PassThrough>,
168
+ );
169
+ });
170
+
171
+ expect(store).toMatchInlineSnapshot(`
172
+ [root]
173
+ ▾ <PassThrough key="e">
174
+ ▾ <PassThrough>
175
+ <p>
176
+ ▾ <PassThrough>
177
+ <div>
178
+ `);
179
+
180
+ await act(() => {
181
+ render(
182
+ <PassThrough key="e" name="e">
183
+ <IgnoreMePassthrough key="e2">
184
+ <PassThrough name="e-child-two">
185
+ <div>e2</div>
186
+ </PassThrough>
187
+ </IgnoreMePassthrough>
188
+ <IgnoreMePassthrough key="e1">
189
+ <PassThrough name="e-child-one">
190
+ <p>e1</p>
191
+ </PassThrough>
192
+ </IgnoreMePassthrough>
193
+ </PassThrough>,
194
+ );
195
+ });
196
+
197
+ expect(store).toMatchInlineSnapshot(`
198
+ [root]
199
+ ▾ <PassThrough key="e">
200
+ ▾ <PassThrough>
201
+ <div>
202
+ ▾ <PassThrough>
203
+ <p>
204
+ `);
205
+ });
206
+
207
describe('StrictMode compliance', () => {
208
it('should mark strict root elements as strict', async () => {
209
const App = () => <Component />;
packages/react-devtools-shared/src/backend/fiber/renderer.js
+5
@@ -3612,6 +3612,11 @@ export function attach(
3612
shouldResetChildren = true;
3613
}
3614
} else if (prevChild !== null && shouldFilterFiber(nextChild)) {
3615
+ // The filtered instance could've reordered.
3616
+ if (prevChild !== prevChildAtSameIndex) {
3617
+ shouldResetChildren = true;
3618
+ }
3619
+
3620
// If this Fiber should be filtered, we need to still update its children.
3621
// This relies on an alternate since we don't have an Instance with the previous
3622
// child on it. Ideally, the reconciliation wouldn't need previous Fibers that