Skip to content

Commit 1a19170

Browse files
authored
[refactor] Add element type for Activity (#32499)
This PR separates Activity to it's own element type separate from Offscreen. The goal is to allow us to add Activity element boundary semantics during hydration similar to Suspense semantics, without impacting the Offscreen behavior in suspended children.
1 parent 99563e9 commit 1a19170

18 files changed

+504
-95
lines changed

Diff for: packages/react-devtools-shared/src/__tests__/storeComponentFilters-test.js

+166
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,172 @@ describe('Store component filters', () => {
135135
});
136136

137137
// @reactVersion >= 16.0
138+
it('should filter Suspense', async () => {
139+
const Suspense = React.Suspense;
140+
await actAsync(async () =>
141+
render(
142+
<React.Fragment>
143+
<Suspense>
144+
<div>Visible</div>
145+
</Suspense>
146+
<Suspense>
147+
<div>Hidden</div>
148+
</Suspense>
149+
</React.Fragment>,
150+
),
151+
);
152+
153+
expect(store).toMatchInlineSnapshot(`
154+
[root]
155+
▾ <Suspense>
156+
<div>
157+
▾ <Suspense>
158+
<div>
159+
`);
160+
161+
await actAsync(
162+
async () =>
163+
(store.componentFilters = [
164+
utils.createElementTypeFilter(Types.ElementTypeActivity),
165+
]),
166+
);
167+
168+
expect(store).toMatchInlineSnapshot(`
169+
[root]
170+
▾ <Suspense>
171+
<div>
172+
▾ <Suspense>
173+
<div>
174+
`);
175+
176+
await actAsync(
177+
async () =>
178+
(store.componentFilters = [
179+
utils.createElementTypeFilter(Types.ElementTypeActivity, false),
180+
]),
181+
);
182+
183+
expect(store).toMatchInlineSnapshot(`
184+
[root]
185+
▾ <Suspense>
186+
<div>
187+
▾ <Suspense>
188+
<div>
189+
`);
190+
});
191+
192+
it('should filter Activity', async () => {
193+
const Activity = React.unstable_Activity;
194+
195+
if (Activity != null) {
196+
await actAsync(async () =>
197+
render(
198+
<React.Fragment>
199+
<Activity mode="visible">
200+
<div>Visible</div>
201+
</Activity>
202+
<Activity mode="hidden">
203+
<div>Hidden</div>
204+
</Activity>
205+
</React.Fragment>,
206+
),
207+
);
208+
209+
expect(store).toMatchInlineSnapshot(`
210+
[root]
211+
▾ <Activity>
212+
<div>
213+
▾ <Activity>
214+
<div>
215+
`);
216+
217+
await actAsync(
218+
async () =>
219+
(store.componentFilters = [
220+
utils.createElementTypeFilter(Types.ElementTypeActivity),
221+
]),
222+
);
223+
224+
expect(store).toMatchInlineSnapshot(`
225+
[root]
226+
<div>
227+
<div>
228+
`);
229+
230+
await actAsync(
231+
async () =>
232+
(store.componentFilters = [
233+
utils.createElementTypeFilter(Types.ElementTypeActivity, false),
234+
]),
235+
);
236+
237+
expect(store).toMatchInlineSnapshot(`
238+
[root]
239+
▾ <Activity>
240+
<div>
241+
▾ <Activity>
242+
<div>
243+
`);
244+
}
245+
});
246+
247+
it('should filter ViewTransition', async () => {
248+
const ViewTransition = React.unstable_ViewTransition;
249+
250+
if (ViewTransition != null) {
251+
await actAsync(async () =>
252+
render(
253+
<React.Fragment>
254+
<ViewTransition>
255+
<div>Visible</div>
256+
</ViewTransition>
257+
<ViewTransition>
258+
<div>Hidden</div>
259+
</ViewTransition>
260+
</React.Fragment>,
261+
),
262+
);
263+
264+
expect(store).toMatchInlineSnapshot(`
265+
[root]
266+
▾ <ViewTransition>
267+
<div>
268+
▾ <ViewTransition>
269+
<div>
270+
`);
271+
272+
await actAsync(
273+
async () =>
274+
(store.componentFilters = [
275+
utils.createElementTypeFilter(Types.ElementTypeActivity),
276+
]),
277+
);
278+
279+
expect(store).toMatchInlineSnapshot(`
280+
[root]
281+
▾ <ViewTransition>
282+
<div>
283+
▾ <ViewTransition>
284+
<div>
285+
`);
286+
287+
await actAsync(
288+
async () =>
289+
(store.componentFilters = [
290+
utils.createElementTypeFilter(Types.ElementTypeActivity, false),
291+
]),
292+
);
293+
294+
expect(store).toMatchInlineSnapshot(`
295+
[root]
296+
▾ <ViewTransition>
297+
<div>
298+
▾ <ViewTransition>
299+
<div>
300+
`);
301+
}
302+
});
303+
138304
it('should ignore invalid ElementTypeRoot filter', async () => {
139305
const Component = () => <div>Hi</div>;
140306

Diff for: packages/react-devtools-shared/src/backend/fiber/DevToolsFiberComponentStack.js

+7
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export function describeFiber(
4444
ForwardRef,
4545
ClassComponent,
4646
ViewTransitionComponent,
47+
ActivityComponent,
4748
} = workTagMap;
4849

4950
switch (workInProgress.tag) {
@@ -60,6 +61,8 @@ export function describeFiber(
6061
return describeBuiltInComponentFrame('SuspenseList');
6162
case ViewTransitionComponent:
6263
return describeBuiltInComponentFrame('ViewTransition');
64+
case ActivityComponent:
65+
return describeBuiltInComponentFrame('Activity');
6366
case FunctionComponent:
6467
case IndeterminateComponent:
6568
case SimpleMemoComponent:
@@ -154,6 +157,7 @@ export function getOwnerStackByFiberInDev(
154157
SuspenseComponent,
155158
SuspenseListComponent,
156159
ViewTransitionComponent,
160+
ActivityComponent,
157161
} = workTagMap;
158162
try {
159163
let info = '';
@@ -184,6 +188,9 @@ export function getOwnerStackByFiberInDev(
184188
case ViewTransitionComponent:
185189
info += describeBuiltInComponentFrame('ViewTransition');
186190
break;
191+
case ActivityComponent:
192+
info += describeBuiltInComponentFrame('Activity');
193+
break;
187194
}
188195

189196
let owner: void | null | Fiber | ReactComponentInfo = workInProgress;

Diff for: packages/react-devtools-shared/src/backend/fiber/renderer.js

+12
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
ElementTypeSuspenseList,
2929
ElementTypeTracingMarker,
3030
ElementTypeViewTransition,
31+
ElementTypeActivity,
3132
ElementTypeVirtual,
3233
StrictMode,
3334
} from 'react-devtools-shared/src/frontend/types';
@@ -385,6 +386,7 @@ export function getInternalReactConstants(version: string): {
385386
YieldComponent: -1, // Removed
386387
Throw: 29,
387388
ViewTransitionComponent: 30, // Experimental
389+
ActivityComponent: 31,
388390
};
389391
} else if (gte(version, '17.0.0-alpha')) {
390392
ReactTypeOfWork = {
@@ -421,6 +423,7 @@ export function getInternalReactConstants(version: string): {
421423
YieldComponent: -1, // Removed
422424
Throw: -1, // Doesn't exist yet
423425
ViewTransitionComponent: -1, // Doesn't exist yet
426+
ActivityComponent: -1, // Doesn't exist yet
424427
};
425428
} else if (gte(version, '16.6.0-beta.0')) {
426429
ReactTypeOfWork = {
@@ -457,6 +460,7 @@ export function getInternalReactConstants(version: string): {
457460
YieldComponent: -1, // Removed
458461
Throw: -1, // Doesn't exist yet
459462
ViewTransitionComponent: -1, // Doesn't exist yet
463+
ActivityComponent: -1, // Doesn't exist yet
460464
};
461465
} else if (gte(version, '16.4.3-alpha')) {
462466
ReactTypeOfWork = {
@@ -493,6 +497,7 @@ export function getInternalReactConstants(version: string): {
493497
YieldComponent: -1, // Removed
494498
Throw: -1, // Doesn't exist yet
495499
ViewTransitionComponent: -1, // Doesn't exist yet
500+
ActivityComponent: -1, // Doesn't exist yet
496501
};
497502
} else {
498503
ReactTypeOfWork = {
@@ -529,6 +534,7 @@ export function getInternalReactConstants(version: string): {
529534
YieldComponent: 9,
530535
Throw: -1, // Doesn't exist yet
531536
ViewTransitionComponent: -1, // Doesn't exist yet
537+
ActivityComponent: -1, // Doesn't exist yet
532538
};
533539
}
534540
// **********************************************************
@@ -572,6 +578,7 @@ export function getInternalReactConstants(version: string): {
572578
TracingMarkerComponent,
573579
Throw,
574580
ViewTransitionComponent,
581+
ActivityComponent,
575582
} = ReactTypeOfWork;
576583

577584
function resolveFiberType(type: any): $FlowFixMe {
@@ -622,6 +629,8 @@ export function getInternalReactConstants(version: string): {
622629
}
623630

624631
switch (tag) {
632+
case ActivityComponent:
633+
return 'Activity';
625634
case CacheComponent:
626635
return 'Cache';
627636
case ClassComponent:
@@ -892,6 +901,7 @@ export function attach(
892901
StrictModeBits,
893902
} = getInternalReactConstants(version);
894903
const {
904+
ActivityComponent,
895905
CacheComponent,
896906
ClassComponent,
897907
ContextConsumer,
@@ -1565,6 +1575,8 @@ export function attach(
15651575
const {type, tag} = fiber;
15661576

15671577
switch (tag) {
1578+
case ActivityComponent:
1579+
return ElementTypeActivity;
15681580
case ClassComponent:
15691581
case IncompleteClassComponent:
15701582
return ElementTypeClass;

Diff for: packages/react-devtools-shared/src/backend/types.js

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export type WorkTagMap = {
7777
YieldComponent: WorkTag,
7878
Throw: WorkTag,
7979
ViewTransitionComponent: WorkTag,
80+
ActivityComponent: WorkTag,
8081
};
8182

8283
export type HostInstance = Object;

Diff for: packages/react-devtools-shared/src/devtools/views/Settings/ComponentsSettings.js

+6
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ export default function ComponentsSettings({
472472
((parseInt(currentTarget.value, 10): any): ElementType),
473473
)
474474
}>
475+
{/* TODO: currently only experimental, only list this if it's available */}
476+
{/*<option value={ElementTypeActivity}>activity</option>*/}
475477
<option value={ElementTypeClass}>class</option>
476478
<option value={ElementTypeContext}>context</option>
477479
<option value={ElementTypeFunction}>function</option>
@@ -485,6 +487,10 @@ export default function ComponentsSettings({
485487
<option value={ElementTypeOtherOrUnknown}>other</option>
486488
<option value={ElementTypeProfiler}>profiler</option>
487489
<option value={ElementTypeSuspense}>suspense</option>
490+
{/* TODO: currently only experimental, only list this if it's available */}
491+
{/*<option value={ElementTypeViewTransition}>*/}
492+
{/* view transition*/}
493+
{/*</option>*/}
488494
</select>
489495
)}
490496
{(componentFilter.type === ComponentFilterLocation ||

Diff for: packages/react-devtools-shared/src/frontend/types.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export const ElementTypeSuspenseList = 13;
5050
export const ElementTypeTracingMarker = 14;
5151
export const ElementTypeVirtual = 15;
5252
export const ElementTypeViewTransition = 16;
53+
export const ElementTypeActivity = 17;
5354

5455
// Different types of elements displayed in the Elements tree.
5556
// These types may be used to visually distinguish types,
@@ -68,7 +69,8 @@ export type ElementType =
6869
| 13
6970
| 14
7071
| 15
71-
| 16;
72+
| 16
73+
| 17;
7274

7375
// WARNING
7476
// The values below are referenced by ComponentFilters (which are saved via localStorage).

Diff for: packages/react-reconciler/src/ReactFiber.js

+15
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import {
7171
TracingMarkerComponent,
7272
Throw,
7373
ViewTransitionComponent,
74+
ActivityComponent,
7475
} from './ReactWorkTags';
7576
import {OffscreenVisible} from './ReactFiberActivityComponent';
7677
import {getComponentNameFromOwner} from 'react-reconciler/src/getComponentNameFromFiber';
@@ -107,6 +108,7 @@ import {
107108
REACT_TRACING_MARKER_TYPE,
108109
REACT_ELEMENT_TYPE,
109110
REACT_VIEW_TRANSITION_TYPE,
111+
REACT_ACTIVITY_TYPE,
110112
} from 'shared/ReactSymbols';
111113
import {TransitionTracingMarker} from './ReactFiberTracingMarkerComponent';
112114
import {
@@ -588,6 +590,8 @@ export function createFiberFromTypeAndProps(
588590
}
589591
} else {
590592
getTag: switch (type) {
593+
case REACT_ACTIVITY_TYPE:
594+
return createFiberFromActivity(pendingProps, mode, lanes, key);
591595
case REACT_FRAGMENT_TYPE:
592596
return createFiberFromFragment(pendingProps.children, mode, lanes, key);
593597
case REACT_STRICT_MODE_TYPE:
@@ -865,6 +869,17 @@ export function createFiberFromOffscreen(
865869
fiber.stateNode = primaryChildInstance;
866870
return fiber;
867871
}
872+
export function createFiberFromActivity(
873+
pendingProps: OffscreenProps,
874+
mode: TypeOfMode,
875+
lanes: Lanes,
876+
key: null | string,
877+
): Fiber {
878+
const fiber = createFiber(ActivityComponent, pendingProps, key, mode);
879+
fiber.elementType = REACT_ACTIVITY_TYPE;
880+
fiber.lanes = lanes;
881+
return fiber;
882+
}
868883

869884
export function createFiberFromViewTransition(
870885
pendingProps: ViewTransitionProps,

0 commit comments

Comments
 (0)