Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[refactor] Add element type for Activity #32499

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

rickhanlonii
Copy link
Member

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.

@github-actions github-actions bot added the React Core Team Opened by a member of the React Core Team label Mar 2, 2025
mode,
renderLanes,
);
primaryChildFragment.ref = workInProgress.ref;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sebmarkbage currently I'm just forwarding the ref to the Offscreen element child, and not changing how manual mode works. As a follow up, should we make it so the ref is only exposed to the Activity element, which finds the Offscreen child to toggle visibility?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

@react-sizebot
Copy link

react-sizebot commented Mar 2, 2025

Comparing: 2980f27...84f0996

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.68 kB 6.68 kB = 1.83 kB 1.83 kB
oss-stable/react-dom/cjs/react-dom-client.production.js +0.23% 518.24 kB 519.45 kB +0.16% 92.43 kB 92.58 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.69 kB 6.69 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js +0.21% 570.57 kB 571.78 kB +0.13% 101.56 kB 101.69 kB
facebook-www/ReactDOM-prod.classic.js +0.18% 638.06 kB 639.23 kB +0.13% 112.28 kB 112.42 kB
facebook-www/ReactDOM-prod.modern.js +0.18% 628.38 kB 629.51 kB +0.11% 110.70 kB 110.82 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable-semver/react-is/cjs/react-is.production.js +1.97% 4.57 kB 4.66 kB +1.91% 1.10 kB 1.12 kB
oss-stable/react-is/cjs/react-is.production.js +1.97% 4.57 kB 4.66 kB +1.91% 1.10 kB 1.12 kB
facebook-react-native/react-is/cjs/ReactIs-prod.js +1.97% 4.58 kB 4.67 kB +1.82% 1.10 kB 1.12 kB
facebook-react-native/react-is/cjs/ReactIs-profiling.js +1.97% 4.58 kB 4.67 kB +1.82% 1.10 kB 1.12 kB
oss-experimental/react-is/cjs/react-is.production.js +1.95% 4.61 kB 4.70 kB +1.81% 1.11 kB 1.13 kB
facebook-react-native/react-is/cjs/ReactIs-dev.js +1.93% 5.07 kB 5.17 kB +1.60% 1.13 kB 1.15 kB
oss-stable-semver/react-is/cjs/react-is.development.js +1.91% 5.12 kB 5.22 kB +1.56% 1.15 kB 1.17 kB
oss-stable/react-is/cjs/react-is.development.js +1.91% 5.12 kB 5.22 kB +1.56% 1.15 kB 1.17 kB
oss-experimental/react-is/cjs/react-is.development.js +1.90% 5.17 kB 5.26 kB +1.73% 1.16 kB 1.18 kB
facebook-www/ReactIs-prod.classic.js +1.50% 5.99 kB 6.08 kB +1.44% 1.39 kB 1.41 kB
facebook-www/ReactIs-prod.modern.js +1.50% 5.99 kB 6.08 kB +1.44% 1.39 kB 1.41 kB
facebook-www/ReactIs-dev.classic.js +1.49% 6.59 kB 6.69 kB +1.41% 1.42 kB 1.44 kB
facebook-www/ReactIs-dev.modern.js +1.49% 6.59 kB 6.69 kB +1.41% 1.42 kB 1.44 kB
oss-stable-semver/react-art/cjs/react-art.production.js +0.40% 302.23 kB 303.44 kB +0.27% 51.41 kB 51.55 kB
oss-stable/react-art/cjs/react-art.production.js +0.40% 302.31 kB 303.52 kB +0.27% 51.44 kB 51.58 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-prod.js +0.39% 334.54 kB 335.85 kB +0.33% 58.22 kB 58.41 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.production.js +0.39% 313.80 kB 315.01 kB +0.25% 54.95 kB 55.09 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.production.js +0.39% 313.88 kB 315.09 kB +0.26% 54.98 kB 55.12 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.production.js +0.39% 314.05 kB 315.26 kB +0.26% 55.01 kB 55.15 kB
oss-experimental/react-art/cjs/react-art.production.js +0.37% 326.42 kB 327.63 kB +0.22% 55.65 kB 55.78 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-profiling.js +0.37% 356.71 kB 358.03 kB +0.29% 61.39 kB 61.57 kB
react-native/implementations/ReactFabric-prod.js +0.35% 360.20 kB 361.47 kB +0.28% 62.52 kB 62.69 kB
react-native/implementations/ReactNativeRenderer-prod.js +0.35% 367.26 kB 368.53 kB +0.24% 63.67 kB 63.83 kB
react-native/implementations/ReactFabric-prod.fb.js +0.34% 381.91 kB 383.22 kB +0.24% 66.26 kB 66.42 kB
react-native/implementations/ReactNativeRenderer-prod.fb.js +0.34% 385.44 kB 386.74 kB +0.28% 66.92 kB 67.10 kB
oss-stable-semver/react/cjs/react.react-server.development.js +0.34% 28.90 kB 29.00 kB +0.33% 6.93 kB 6.95 kB
oss-stable/react/cjs/react.react-server.development.js +0.34% 28.92 kB 29.02 kB +0.33% 6.96 kB 6.98 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.production.js +0.33% 392.65 kB 393.96 kB +0.23% 63.60 kB 63.75 kB
oss-stable/react-reconciler/cjs/react-reconciler.production.js +0.33% 392.67 kB 393.99 kB +0.23% 63.62 kB 63.77 kB
react-native/implementations/ReactFabric-profiling.js +0.33% 385.44 kB 386.72 kB +0.26% 66.24 kB 66.41 kB
react-native/implementations/ReactNativeRenderer-profiling.fb.js +0.32% 410.92 kB 412.25 kB +0.25% 70.73 kB 70.91 kB
react-native/implementations/ReactNativeRenderer-profiling.js +0.32% 392.61 kB 393.88 kB +0.25% 67.48 kB 67.64 kB
react-native/implementations/ReactFabric-profiling.fb.js +0.32% 407.42 kB 408.73 kB +0.24% 70.09 kB 70.26 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.profiling.js +0.31% 418.84 kB 420.15 kB +0.23% 67.28 kB 67.44 kB
oss-stable/react-reconciler/cjs/react-reconciler.profiling.js +0.31% 418.86 kB 420.18 kB +0.24% 67.30 kB 67.46 kB
facebook-www/ReactART-prod.classic.js +0.30% 384.59 kB 385.75 kB +0.25% 64.58 kB 64.75 kB
facebook-www/ReactART-prod.modern.js +0.30% 374.64 kB 375.76 kB +0.22% 62.98 kB 63.12 kB
oss-experimental/react-reconciler/cjs/react-reconciler.production.js +0.30% 438.36 kB 439.67 kB +0.20% 70.77 kB 70.91 kB
oss-experimental/react-reconciler/cjs/react-reconciler.profiling.js +0.28% 491.32 kB 492.68 kB +0.22% 78.64 kB 78.81 kB
oss-experimental/react/cjs/react.react-server.development.js +0.27% 35.96 kB 36.05 kB +0.26% 8.50 kB 8.52 kB
facebook-www/ReactReconciler-prod.classic.js +0.25% 497.39 kB 498.62 kB +0.19% 79.46 kB 79.61 kB
facebook-www/ReactReconciler-prod.modern.js +0.24% 487.13 kB 488.31 kB +0.17% 77.84 kB 77.98 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-prod.js +0.24% 543.28 kB 544.59 kB +0.18% 96.42 kB 96.60 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-prod.js +0.24% 548.78 kB 550.09 kB +0.18% 97.50 kB 97.67 kB
oss-stable-semver/react-test-renderer/cjs/react-test-renderer.development.js +0.24% 558.06 kB 559.38 kB +0.18% 90.65 kB 90.82 kB
oss-experimental/react-test-renderer/cjs/react-test-renderer.development.js +0.24% 558.09 kB 559.41 kB +0.18% 90.67 kB 90.83 kB
oss-stable/react-test-renderer/cjs/react-test-renderer.development.js +0.24% 558.14 kB 559.46 kB +0.18% 90.68 kB 90.84 kB
oss-stable-semver/react-art/cjs/react-art.development.js +0.24% 560.63 kB 561.95 kB +0.18% 90.24 kB 90.40 kB
oss-stable/react-art/cjs/react-art.development.js +0.24% 560.70 kB 562.03 kB +0.18% 90.26 kB 90.43 kB
oss-stable-semver/react-dom/cjs/react-dom-client.production.js +0.23% 518.12 kB 519.32 kB +0.16% 92.41 kB 92.55 kB
oss-stable/react-dom/cjs/react-dom-client.production.js +0.23% 518.24 kB 519.45 kB +0.16% 92.43 kB 92.58 kB
facebook-react-native/react-test-renderer/cjs/ReactTestRenderer-dev.js +0.23% 599.00 kB 600.38 kB +0.19% 96.55 kB 96.74 kB
facebook-react-native/react-dom/cjs/ReactDOMClient-profiling.js +0.23% 568.14 kB 569.44 kB +0.18% 100.18 kB 100.36 kB
facebook-www/ReactTestRenderer-dev.modern.js +0.23% 576.00 kB 577.32 kB +0.18% 93.72 kB 93.89 kB
facebook-www/ReactTestRenderer-dev.classic.js +0.23% 576.00 kB 577.33 kB +0.18% 93.72 kB 93.89 kB
facebook-react-native/react-dom/cjs/ReactDOMProfiling-profiling.js +0.23% 574.08 kB 575.39 kB +0.17% 101.34 kB 101.52 kB
oss-stable-semver/react-dom/cjs/react-dom-profiling.profiling.js +0.22% 548.52 kB 549.73 kB +0.14% 97.11 kB 97.25 kB
oss-stable/react-dom/cjs/react-dom-profiling.profiling.js +0.22% 548.65 kB 549.85 kB +0.14% 97.14 kB 97.28 kB
oss-stable-semver/react/cjs/react.development.js +0.22% 45.24 kB 45.34 kB +0.21% 10.28 kB 10.30 kB
oss-stable/react/cjs/react.development.js +0.22% 45.27 kB 45.36 kB +0.21% 10.31 kB 10.33 kB
react-native/implementations/ReactFabric-dev.js +0.22% 641.90 kB 643.28 kB +0.19% 104.56 kB 104.76 kB
oss-experimental/react-art/cjs/react-art.development.js +0.21% 620.46 kB 621.78 kB +0.17% 98.70 kB 98.87 kB
react-native/implementations/ReactNativeRenderer-dev.js +0.21% 649.75 kB 651.13 kB +0.19% 105.94 kB 106.15 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js +0.21% 570.57 kB 571.78 kB +0.13% 101.56 kB 101.69 kB
oss-experimental/react-dom/cjs/react-dom-unstable_testing.production.js +0.21% 585.30 kB 586.51 kB +0.13% 105.12 kB 105.26 kB
oss-experimental/react/cjs/react.development.js +0.21% 47.01 kB 47.11 kB +0.13% 10.67 kB 10.69 kB
react-native/implementations/ReactFabric-dev.fb.js +0.21% 671.99 kB 673.37 kB +0.16% 109.15 kB 109.32 kB
react-native/implementations/ReactNativeRenderer-dev.fb.js +0.20% 676.45 kB 677.83 kB +0.18% 110.00 kB 110.19 kB
oss-stable-semver/react-reconciler/cjs/react-reconciler.development.js +0.20% 646.66 kB 647.99 kB +0.16% 103.13 kB 103.29 kB
oss-stable/react-reconciler/cjs/react-reconciler.development.js +0.20% 646.69 kB 648.01 kB +0.16% 103.16 kB 103.32 kB
facebook-www/ReactReconciler-dev.modern.js +0.20% 762.24 kB 763.79 kB +0.56% 119.40 kB 120.07 kB
facebook-www/ReactReconciler-dev.classic.js +0.20% 771.45 kB 773.00 kB +0.56% 121.06 kB 121.74 kB

Generated by 🚫 dangerJS against b045f18

@@ -2261,6 +2262,7 @@ function renderElement(
task.keyPath = prevKeyPath;
return;
}
case REACT_ACTIVITY_TYPE:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sebmarkbage this might not be correct - to match the client it should render an Offscreen element, but I'm not sure how to do that in Fizz. Without that, I think this might cause a hydration error if it's rendered in mode visible?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Offscreen is an implementation detail of Fiber and it will never be noticeable in the HTML so it can't hydrate.

Basically Fizz doesn't need to know about the concept of Offscreen. It doesn't need the same type of code sharing between Suspense and Activity that Fiber needs to manage the complexity. So it can be just forked paths.

In other words, you can just drop REACT_OFFSCREEN_TYPE here and rename it to Activity.

@@ -867,6 +868,46 @@ function deferHiddenOffscreenComponent(
// fork the function.
const updateLegacyHiddenComponent = updateOffscreenComponent;

function updateActivityComponent(
Copy link
Member Author

@rickhanlonii rickhanlonii Mar 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a close review, I have no idea what I'm doing. mountWorkInProgressOffscreenFiber and updateWorkInProgressOffscreenFiber are what Suspense use, so I assume that's how we want to mount and update the children here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea this sounds about right.

@@ -34,6 +34,7 @@ export const REACT_MEMO_TYPE: symbol = Symbol.for('react.memo');
export const REACT_LAZY_TYPE: symbol = Symbol.for('react.lazy');
export const REACT_SCOPE_TYPE: symbol = Symbol.for('react.scope');
export const REACT_OFFSCREEN_TYPE: symbol = Symbol.for('react.offscreen');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still used anywhere or can this be deleted now?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically the idea is that React internally only refers to Offscreen using the tag. It should never have to look at .type or .elementType. The createFiberFromOffscreen should exclude it since it's not needed for the elementType. It can be null. elementType is only used for reconciliation against new elements but since Offscreen is only created internally it should never need to be reconciled. And so it should be impossible to create this symbol an all the cases that try to match it.

@@ -1480,6 +1488,7 @@ export function attach(
return true;
case HostPortal:
case HostText:
case ActivityComponent:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is actually a good example for why we need this to be a separate wrapper. Because this should not be ignored. It should be visible in React DevTools because it's not an internal implementation detail but actually something the user renders.

The reason OffscreenComponent was hidden was because it's an internal of Suspense and it would be very noisy to show it everywhere but the Activity one we actually want to show.

So this should actually go into one of the other paths (and be filterable as a "type" in the filters).

This could be done as a follow up though.

@@ -595,6 +597,8 @@ export function createFiberFromTypeAndProps(
}
} else {
getTag: switch (type) {
case REACT_ACTIVITY_TYPE:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we should be able to delete the Offscreen case?

key: null | string,
): Fiber {
const fiber = createFiber(ActivityComponent, pendingProps, key, mode);
fiber.elementType = REACT_ACTIVITY_TYPE;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not actually right. This was wrong for Offscreen too.

In the case of const LazyActivity = React.lazy(async () => ({ default: React.Activity })); <LazyActivity /> the elementType should be the Lazy wrapper object. Otherwise reconciliation won't work correctly.

In fact, it looks like we get this wrong for all these built-ins so wrapping them would yield the wrong reconciliation.

So it's an existing bug that we need to fix separately.

Copy link
Collaborator

@sebmarkbage sebmarkbage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is landable as is but spawns many follow ups from my comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants