Commit Graph

1271 Commits

Author SHA1 Message Date
javache 63af232d46 [react-native] Pull up enableFastAddPropertiesInDiffing check (#33043)
## Summary

We don't need the isArray check for this experiment, as
`fastAddProperties` already does the same. Also renaming
slowAddProperties to make it clearer we can fully remove this codepath
once fastAddProperties is fully rolled out.

## How did you test this change?

```
yarn test packages/react-native-renderer -r=xplat --variant=true
```

DiffTrain build for [0038c501a3](https://github.com/facebook/react/commit/0038c501a307e5ddc0cb80027e55740ddda09520)
2025-04-29 03:16:29 -07:00
sebmarkbage 520902d9c2 Implement Partial Hydration for Activity (#32863)
Stacked on #32862 and #32842.

This means that Activity boundaries now act as boundaries which can have
their effects mounted independently. Just like Suspense boundaries, we
hydrate the outer content first and then start hydrating the content in
an Offscreen lane. Flowing props or interacting with the content
increases the priority just like Suspense boundaries.

This skips emitting even the comments for `<Activity mode="hidden">` so
we don't hydrate those. Instead those are deferred to a later client
render.

The implementation are just forked copies of the SuspenseComponent
branches and then carefully going through each line and tweaking it.

The main interesting bit is that, unlike Suspense, Activity boundaries
don't have fallbacks so all those branches where you might commit a
suspended tree disappears. Instead, if something suspends while
hydration, we can just leave the dehydrated content in place. However,
if something does suspend during client rendering then it should bubble
up to the parent. Therefore, we have to be careful to only
pushSuspenseHandler when hydrating. That's really the main difference.

This just uses the existing basic Activity tests but I've started work
on port all of the applicable Suspense tests in SelectiveHydration-test
and PartialHydration-test to Activity versions.

DiffTrain build for [3ef31d196a](https://github.com/facebook/react/commit/3ef31d196a83e45d4c70b300a265a9c657c386b4)
2025-04-22 18:07:47 -07:00
sebmarkbage 1ed5cd364a Implement ActivityInstance in FiberConfigDOM (#32842)
Stacked on #32851 and #32900.

This implements the equivalent Configs for ActivityInstance as we have
for SuspenseInstance. These can be implemented as comments but they
don't have to be and can be implemented differently in the renderer.

This seems like a lot duplication but it's actually ends mostly just
calling the same methods underneath and the wrappers compiles out.

This doesn't leave the Activity dehydrated yet. It just hydrates into it
immediately.

DiffTrain build for [17f88c80ed](https://github.com/facebook/react/commit/17f88c80ed20b4e5f21255d9e1268542a2fbc1bd)
2025-04-22 16:50:48 -07:00
sebmarkbage b480230f1b Set hidden Offscreen to the shellBoundary regardless of previous state (#32844)
I think this was probably just copy-paste from the Suspense path.

It shouldn't matter what the previous state of an Offscreen boundary
was. What matters is that it's now hidden and therefore if it suspends,
we can just leave it as is without the tree becoming inconsistent.

DiffTrain build for [3fbd6b7b50](https://github.com/facebook/react/commit/3fbd6b7b50e3a174883633695586b892249e5635)
2025-04-22 16:45:55 -07:00
sebmarkbage f75bdfdeac Hide/unhide the content of dehydrated suspense boundaries if they resuspend (#32900)
Found this bug while working on Activity. There's a weird edge case when
a dehydrated Suspense boundary is a direct child of another Suspense
boundary which is hydrated but then it resuspends without forcing the
inner one to hydrate/delete.

It used to just leave that in place because hiding/unhiding didn't deal
with dehydrated fragments.

Not sure this is really worth fixing.

DiffTrain build for [ebf7318e87](https://github.com/facebook/react/commit/ebf7318e87cf2e10b6bd9a6bb0ad8bf6f6186f80)
2025-04-22 16:35:26 -07:00
sebmarkbage 6c77e921d6 Don't try to hydrate a hidden Offscreen tree (#32862)
I found a bug even before the Activity hydration stuff.

If we're hydrating an Offscreen boundary in its "hidden" state it won't
have any content to hydrate so will trigger hydration errors (which are
then eaten by the Offscreen boundary itself). Leaving it not prewarmed.

This doesn't happen in the simple case because we'd be hydrating at a
higher priority than Offscreen at the root, and those are deferred to
Offscreen by not having higher priority. However, we've hydrating at the
Offscreen priority, which we do inside Suspense boundaries, then it
tries to hydrate against an empty set.

I ended up moving this to the Activity boundary in a future PR since
it's the SSR side that decided where to not render something and it only
has a concept of Activity, no Offscreen.

https://github.com/facebook/react/pull/32863/commits/1dc05a5e2222e18fc3a2062ee1bd957109e21344#diff-d5166797ebbc5b646a49e6a06a049330ca617985d7a6edf3ad1641b43fde1ddfR1111

DiffTrain build for [b04254fdce](https://github.com/facebook/react/commit/b04254fdcee30871760301f34236ee0dfadf86ab)
2025-04-15 14:49:57 -07:00
sebmarkbage a0c6870f0c Warn if you pass a hidden prop to Activity (#32916)
Since `hidden` is a prop on arbitrary DOM elements it's a common mistake
to think that it would also work that way on `<Activity>` but it
doesn't. In fact, we even had this mistakes in our own tests.

Maybe there's an argument that we should actually just support it but we
also have more modes planned.

So this adds a warning. It should also already be covered by TypeScript.

DiffTrain build for [539bbdbd86](https://github.com/facebook/react/commit/539bbdbd86d9cd342aabde4cb08e398751789103)
2025-04-15 14:23:51 -07:00
sebmarkbage 609b3ae8de Try not. Do... or do not. Hydrate Suspense Boundaries. (#32851)
Assertively claim a SuspenseInstance. We already know we're hydrating.

If there's no match, it throws anyway. So there's no other code path.

DiffTrain build for [961b625ab5](https://github.com/facebook/react/commit/961b625ab5d180180e836e0c7b221789f0ee336b)
2025-04-11 07:58:07 -07:00
sebmarkbage 0e2734eb2f Emit Preamble Contribution inline instead of the end of a boundary (#32850)
This lets us write them early in the render phase.

This should be safe because even if we write them deeply, then they
still can't be wrapped by a element because then they'd no longer be in
the document scope anymore. They end up flat in the body and so when we
search the content we'll discover them.

DiffTrain build for [8a3c5e1a8d](https://github.com/facebook/react/commit/8a3c5e1a8d1d89a68ca36c6959c1f253710f6cef)
2025-04-10 16:47:41 -07:00
rubennorte 3aee0d9b2a [RN] Map Fabric priorities to reconciler priorities correctly (#32847)
## Summary

This fixes how we map priorities between Fabric and the React
reconciler. At the moment, we're only considering default and discrete
priorities, when there's a larger range of priorities available.

In Fabric, we'll test supporting additional priorities soon. For that
test to do something useful, we need the new priorities to be mapped to
reconciler priorities correctly, which is what this change is done.

> [!IMPORTANT]
> At the moment, this is a no-op because Fabric is only reporting
default and discrete event priorities.

## How did you test this change?

Will test e2e on React Native on top of
https://github.com/facebook/react-native/pull/50627

The changes are gated in React Native, so we'll use that feature flag to
test this.

DiffTrain build for [5e9b48778c](https://github.com/facebook/react/commit/5e9b48778c83dd8b2c63966db3e23abefebe910c)
2025-04-10 09:40:48 -07:00
sebmarkbage 0a20a318f6 Move Built-in Props Types to React Types (#32841)
Stacked on #32838.

We don't always type the Props of built-ins. This adds typing for most
of the built-ins.

When we did type them, we used to put it in the `ReactFiber...Component`
files but any public API like this can be implemented in other renderers
too such as Fizz. So I moved them to `shared/ReactTypes` which is where
we put other public API types (that are not already built-in to Flow).
That way Fizz can import them and assert properly when it accesses the
props.

DiffTrain build for [c44e4a2505](https://github.com/facebook/react/commit/c44e4a250557e53b120e40db8b01fb5fd93f1e35)
2025-04-09 19:52:23 -07:00
sebmarkbage 6ea2efb3ca Clarify that there's three different kinds of OffscreenProps (#32838)
ActivityProps - Public API
LegacyHiddenProps - Public Legacy API
OffscreenProps - Internal implementation detail

DiffTrain build for [31ecc9804a](https://github.com/facebook/react/commit/31ecc9804a3f263033611f069774e50059c0743a)
2025-04-09 19:31:01 -07:00
sebmarkbage 32ddf34caf Enable Suspensey Images inside <ViewTransition> subtrees (#32820)
Even if the `enableSuspenseyImages` flag is off.

Started View Transitions already wait for Suspensey Fonts and this is
another Suspensey feature that is even more important for View
Transitions - even though we eventually want it all the time. So this
uses `<ViewTransition>` as an early opt-in for that tree into Suspensey
Images, which we can ship in a minor.

If you're doing an update inside a ViewTransition then we're eligible to
start a ViewTransition in any Transition that might suspend. Even if
that doesn't end up animating after all, we still consider it Suspensey.
We could try to suspend inside the startViewTransition but that's not
how it would work with `enableSuspenseyImages` on and we can't do that
for startGestureTransition.

Even so we still need some opt-in to trigger the Suspense fallback even
before we know whether we'll animate or not. So the simple solution is
just that `<ViewTransition>` opts in the whole subtree into Suspensey
Images in general.

In this PR I disable `enableSuspenseyImages` in experimental so that we
can instead test the path that only enables it inside `<ViewTransition>`
tree since that's the path that would next graduate to a minor.

DiffTrain build for [8da36d0508](https://github.com/facebook/react/commit/8da36d0508e83dd342ddbb98cb18f0606fd4045b)
2025-04-08 15:01:37 -07:00
sebmarkbage f0a6a89dea Allow Passing Blob/File/MediaSource/MediaStream to src of <img>, <video> and <audio> (#32828)
Behind the `enableSrcObject` flag. This is revisiting a variant of what
was discussed in #11163.

Instead of supporting the [`srcObject`
property](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/srcObject)
as a separate name, this adds an overload of `src` to allow objects to
be passed. The DOM needs to add separate properties for the object forms
since you read back but it doesn't make sense for React's write-only API
to do that. Similar to how we'll like add an overload for
`popoverTarget` instead of calling it `popoverTargetElement` and how
`style` accepts an object and it's not `styleObject={{...}}`.

There are a number of reason to revisit this.

- It's just way more convenient to have this built-in and it makes
conceptual sense. We typically support declarative APIs and polyfill
them when necessary.
- RSC supports Blobs and by having it built-in you don't need a Client
Component wrapper to render it where as doing it with effects would
require more complex wrappers. By picking Blobs over base64,
client-navigations can use the more optimized binary encoding in the RSC
protocol.
- The timing aspect of coordinating it with Suspensey images and image
decoding is a bit tricky to get right because if you set it in an effect
it's too late because you've already rendered it.
- SSR gets complicated when done in user space because you have to
handle both branches. Likely with `useSyncExternalStore`.
- By having it built-in we could optimize the payloads shared between
RSC payloads embedded in the HTML and data URLs.

This does not support objects for `<source src>` nor `<img srcset>`.
Those don't really have equivalents in the DOM neither. They're mainly
for picking an option when you don't know programmatically. However, for
this use case you're really better off picking a variant before
generating the blobs.

We may support Response objects in the future too as per
https://github.com/whatwg/fetch/issues/49

DiffTrain build for [ea05b750a5](https://github.com/facebook/react/commit/ea05b750a5374458fc8c74ea0918059c818d1167)
2025-04-08 09:19:00 -07:00
sebmarkbage a4e2d3cbd1 Workaround against display: inline bug in Safari (#32822)
Safari has a bug where if you put a block element inside an inline
element and the inline element has a `view-transition-name` assigned it
finds it as duplicate names.

https://bugs.webkit.org/show_bug.cgi?id=290923

This adds a warning if we detect this scenario in dev mode.

For the case where it renders into a single block, we can model this by
making the parent either `block` or `inline-block` automatically to fix
the issue. So we do that to automatically cover simple cases like
`<a><div>...</div></a>`. This unfortunately causes layout/styling thrash
so we might want to delete it once the bug has been fixed in enough
Safari versions.

DiffTrain build for [365c031fd2](https://github.com/facebook/react/commit/365c031fd2354e94248ed9390f13fe2975b994f6)
2025-04-07 07:15:32 -07:00
acdlite dc32e7cb2c [Bugfix] Infinite uDV loop in popstate event (#32821)
Found a bug that occurs during a specific combination of very subtle
implementation details.

It occurs sometimes (not always) when 1) a transition is scheduled
during a popstate event, and 2) as a result, a new value is passed to an
already-mounted useDeferredValue hook.

The fix is relatively straightforward, and I found it almost
immediately; it took a bit longer to figure out exactly how the scenario
occurred in production and create a test case to simulate it.

Rather than couple the test to the implementation details, I've chosen
to keep it as high-level as possible so that it doesn't break if the
details change. In the future, it might not be trigger the exact set of
internal circumstances anymore, but it could be useful for catching
similar bugs because it represents a realistic real world situation —
namely, switching tabs repeatedly in an app that uses useDeferredValue.

DiffTrain build for [6a7650c75c](https://github.com/facebook/react/commit/6a7650c75c1bc110517bd9b3eefdc66eadbb9cbf)
2025-04-04 21:55:17 -07:00
sebmarkbage 28c3ec94a9 Add Suspensey Images behind a Flag (#32819)
We've known we've wanted this for many years and most of the
implementation was already done for Suspensey CSS. This waits to commit
until images have decoded by default or up to 500ms timeout (same as
suspensey fonts).

It only applies to Transitions, Retries (Suspense), Gesture Transitions
(flag) and Idle (doesn't exist). Sync updates just commit immediately.

`<img loading="lazy" src="..." />` opts out since you explicitly want it
to load lazily in that case.

`<img onLoad={...} src="..." />` also opts out since that implies you're
ok with managing your own reveal.

In the future, we may add an opt in e.g. `<img blocking="render"
src="..." />` that opts into longer timeouts and re-suspends even sync
updates. Perhaps also triggering error boundaries on errors.

The rollout for this would have to go in a major and we may have to
relax the default timeout to not delay too much by default. However, we
can also make this part of `enableViewTransition` so that if you opt-in
by using View Transitions then those animations will suspend on images.
That we could ship in a minor.

DiffTrain build for [efb22d8850](https://github.com/facebook/react/commit/efb22d8850382c3b53c1b2b8d22036d7e6cc9488)
2025-04-04 12:00:08 -07:00
sebmarkbage 7e5dab9fe1 Fix Bugs Measuring Performance Track for Effects (#32815)
This fixes two bugs with commit phase effect tracking.

I missed, or messed up the rebase for, deletion effects when a subtree
was deleted and for passive disconnects when a subtree was hidden.

The other bug is that when I started using self time
(componentEffectDuration) for color and for determining whether to
bother logging an entry, I didn't consider that the component with
effects can have children which end up resetting this duration before we
log. Which lead to most effects not having their components logged since
they almost always have children.

We don't necessarily have to push/pop but we have to store at least one
thing on the stack unfortunately. That's because we have to do the
actual log after the children to get the right end time. So might as
well use the push/pop strategy like the rest of them.

DiffTrain build for [c0f08ae74a](https://github.com/facebook/react/commit/c0f08ae74a46686f5718e9e6c511d27419fd632c)
2025-04-03 20:40:11 -07:00
sebmarkbage 00e3f22c1a Don't shadow EventListenerOptionsOrUseCapture and FocusOptions types (#32801)
These are built-in to Flow.

DiffTrain build for [7a728dffd1](https://github.com/facebook/react/commit/7a728dffd14550cd22e6d8b8514e82435bbeba76)
2025-04-01 11:29:17 -07:00
sebmarkbage 67628e7676 Warn if addTransitionType is called when there are no pending Actions (#32793)
Stacked on #32792.

It's tricky to associate a specific `addTransitionType` call to a
specific `startTransition` call because we don't have `AsyncContext` in
browsers yet. However, we can keep track if there are any async
transitions running at all, and if not, warn. This should cover most
cases.

This also errors when inside a React render which might be a legit way
to associate a Transition Type to a specific render (e.g. based on props
changing) but we want to be a more conservative about allowing that yet.
If we wanted to support calling it in render, we might want to set which
Transition object is currently rendering but it's still tricky if the
render has `async function` components. So it might at least be
restricted to sync components (like Hooks).

DiffTrain build for [deca96520f](https://github.com/facebook/react/commit/deca96520f1e9e804b0e5b0d81563327d9c55521)
2025-04-01 09:28:47 -07:00
sebmarkbage 70814b1b24 Add startGestureTransition API (#32785)
Stacked on #32783. This will replace [the `useSwipeTransition`
API](https://github.com/facebook/react/pull/32373).

Instead, of a special Hook, you can make updates to `useOptimistic`
Hooks within the `startGestureTransition` scope.

```
import {unstable_startGestureTransition as startGestureTransition} from 'react';

const cancel = startGestureTransition(timeline, () => {
  setOptimistic(...);
}, options);
```

There are some downsides to this like you can't define two directions as
once and there's no "standard" direction protocol. It's instead up to
libraries to come up with their own conventions (although we can suggest
some).

The convention is still that a gesture recognizer has two props `action`
and `gesture`. The `gesture` prop is a Gesture concept which now behaves
more like an Action but 1) it can't be async 2) it shouldn't have
side-effects. For example you can't call `setState()` in it except on
`useOptimistic` since those can be reverted if needed. The `action` is
invoked with whatever side-effects you want after the gesture fulfills.

This is isomorphic and not associated with a specific renderer nor root
so it's a bit more complicated.

To implement this I unify with the `ReactSharedInternal.T` property to
contain a regular Transition or a Gesture Transition (the `gesture`
field). The benefit of this unification means that every time we
override this based on some scope like entering `flushSync` we also
override the `startGestureTransition` scope. We just have to be careful
when we read it to check the `gesture` field to know which one it is.
(E.g. I error for setState / requestFormReset.)

The other thing that's unique is the `cancel` return value to know when
to stop the gesture. That cancellation is no longer associated with any
particular Hook. It's more associated with the scope of the
`startGestureTransition`. Since the schedule of whether a particular
gesture has rendered or committed is associated with a root, we need to
somehow associate any scheduled gestures with a root.

We could track which roots we update inside the scope but instead, I
went with a model where I check all the roots and see if there's a
scheduled gesture matching the timeline. This means that you could
"retain" a gesture across roots. Meaning this wouldn't cancel until both
are cancelled:

```
const cancelA = startGestureTransition(timeline, () => {
  setOptimisticOnRootA(...);
}, options);

const cancelB = startGestureTransition(timeline, () => {
  setOptimisticOnRootB(...);
}, options);
```

It's more like it's a global transition than associated with the roots
that were updated.

Optimistic updates mostly just work but I now associate them with a
specific "ScheduledGesture" instance since we can only render one at a
time and so if it's not the current one, we leave it for later.

Clean up of optimistic updates is now lazy rather than when we cancel.
Allowing the cancel closure not to have to be associated with each
particular update.

DiffTrain build for [b286430c8a](https://github.com/facebook/react/commit/b286430c8a585dc2e2e3cc023e7c455ec2b34ab7)
2025-03-31 17:17:22 -07:00
sebmarkbage f8bbdb7c32 Unify BatchConfigTransition and Transition types (#32783)
This is some overdue refactoring. The two types never made sense. It
also should be defined by isomorphic since it defines how it should be
used by renderers rather than isomorphic depending on Fiber.

Clean up hidden classes to be consistent.

Fix missing name due to wrong types. I choose not to invoke the
transition tracing callbacks if there's no name since the name is
required there.

DiffTrain build for [d3b8ff6e58](https://github.com/facebook/react/commit/d3b8ff6e589bcacfd1c9b0aa48c42fd1c93001c1)
2025-03-31 17:05:59 -07:00
rickhanlonii bea1002a4f s/HTML/text for text hydration mismatches (#32763)
DiffTrain build for [3e88e97c11](https://github.com/facebook/react/commit/3e88e97c116c7a1535976f2d4486bbf345476443)
2025-03-26 14:46:01 -07:00
sebmarkbage a28916b5dd Don't flush synchronous work if we're in the middle of a ViewTransition async sequence (#32760)
Starting a View Transition is an async sequence. Since React can get a
sync update in the middle of sequence we sometimes interrupt that
sequence.

Currently, we don't actually cancel the View Transition so it can just
run as a partial. This ensures that we fully skip it when that happens,
as well as warn.

However, it's very easy to trigger this with just a setState in
useLayoutEffect right now. Therefore if we're inside the preparing
sequence of a startViewTransition, this delays work that would've
normally flushed in a microtask. ~Maybe we want to do the same for
Default work already scheduled through a scheduler Task.~ Edit: This was
already done.

`flushSync` currently will still lead to an interrupted View Transition
(with a warning). There's a tradeoff here whether we want to try our
best to preserve the guarantees of `flushSync` or favor the animation.
It's already possible to suspend at the root with `flushSync` which
means it's not always 100% guaranteed to commit anyway. We could treat
it as suspended. But let's see how much this is a problem in practice.

DiffTrain build for [a5297ece62](https://github.com/facebook/react/commit/a5297ece6217f5495cbe38ba58f928b2697b0f99)
2025-03-26 11:50:54 -07:00
poteto 0c4df01e80 [crud] Revert CRUD overload (#32741)
Cleans up this experiment. After some internal experimentation we are
deprioritizing this project for now and may revisit it at a later point.

DiffTrain build for [313332d111](https://github.com/facebook/react/commit/313332d111a2fba2db94c584334d8895e8d73c61)
2025-03-26 09:16:06 -07:00
jackpope 9477f11aa2 Fix ownerStackLimit feature gating for tests (#32726)
https://github.com/facebook/react/pull/32529 added a dynamic flag for
this, but that breaks tests since the flags are not defined everywhere.

However, this is a static value and the flag is only for supporting
existing tests. So we can override it in the test config, and make it
static at built time instead.

DiffTrain build for [f99c9feaf7](https://github.com/facebook/react/commit/f99c9feaf786fbdad0ad8d2d81196a247302dd3c)
2025-03-26 09:09:26 -07:00
SamChou19815 19714aa0d5 [flow] Replace $PropertyType with indexed access type in ReactNativeTypes (#32733)
DiffTrain build for [b59f186011](https://github.com/facebook/react/commit/b59f18601179bb06a2c32a76547fd4929aa1ce9c)
2025-03-24 20:06:45 -07:00
eps1lon 8b3404306d Stop creating Owner Stacks if many have been created recently (#32529)
Co-authored-by: Jack Pope <jackpope1@gmail.com>

DiffTrain build for [4a9df08157](https://github.com/facebook/react/commit/4a9df08157f001c01b078d259748512211233dcf)
2025-03-23 15:53:15 -07:00
poteto 7c8cabc1cf [ci] Add missing permissions to runtime_commit_artifacts.yml (#32710)
Turns out we need permissions to write to `contents` after all.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32710).
* #32711
* __->__ #32710

DiffTrain build for [de4aad5ba6](https://github.com/facebook/react/commit/de4aad5ba693be099b215b5819b5f25d05051a84)
2025-03-21 15:07:21 -07:00
rickhanlonii 8a468f1a72 [refactor] move isValidElementType to react-is (#32518)
DiffTrain build for [b630219b13](https://github.com/facebook/react/commit/b630219b1377f3117036b1c6118676c16fdb21b7)
2025-03-20 14:01:27 -07:00
poteto 1f072d9af9 [ci] Fix Will commit these changes www step (#32681)
Unlike the fbsource version of the step, www doesn't add any changes so
the `force` input doesn't actually work

DiffTrain build for [ff8f6f21f7](https://github.com/facebook/react/commit/ff8f6f21f756c81fba284557357eb6e6ce765149)
2025-03-19 15:38:03 -07:00
poteto af02928d40 [ci] Properly format commit message take 2 (#32673)
We need to use the commit message from `main`, not the builds branch

DiffTrain build for [b0446ff06a](https://github.com/facebook/react/commit/b0446ff06a484127412638c2be9a0382c6f3a84b)
2025-03-19 11:19:28 -07:00
sebmarkbage 861b14904a Measure Updated ViewTransition Boundaries (#32653)
This does the same thing for  that we did
for  in
https://github.com/facebook/react/pull/32612/commits/e3cbaffef05c7b476c07f7495e06788a9503e636.
If a boundary hasn't mutated and didn't change in size, we mark it for
cancellation. Otherwise we add names to it. The different from the
CommitViewTransition path is that the old names are added to the
clones so this is the first time the new names.

Now we also cancel any boundaries that were unchanged. So now the root
no longer animates. We still have to clone them. There are other
optimizations that can avoid cloning but once we've done all the layouts
we can still cancel the running animation and let them just be the
regular content if they didn't change. Just like the regular
fire-and-forget path.

This also fixes the measurement so that we measure clones by adjusting
their position back into the viewport.

This actually surfaces a bug in Safari that was already in #32612. It
turns out that the old names aren't picked up for some reason and so in
Safari they looked more like a cross-fade than what #32612 was supposed
to fix. However, now that bug is even more apparent because they
actually just disappear in Safari. I'm not sure what that bug is but
it's unrelated to this PR so will fix that separately.

DiffTrain build for [3c3696d554](https://github.com/facebook/react/commit/3c3696d5548c8a67f2332fd78332b9366abaf2f9)
2025-03-17 18:45:36 -07:00
rickhanlonii cfa201d0ae [bug] Fix component name for Portal and add tests (#32640)
Based off: https://github.com/facebook/react/pull/32499

While looking into `React.lazy` issues for built-ins, I noticed we
already error for `lazy` with build-ins, but we don't have any tests for
`getComponentNameFromType` using all the built-ins. This may be
something we should handle, but for now we should at least have tests.

Here's why: while writing tests, I noticed we check `type` instead of
`$$typeof` for portals:

https://github.com/facebook/react/blob/9cdf8a99edcfd94d7420835ea663edca04237527/packages/react-reconciler/src/ReactPortal.js#L25-L32

This PR adds tests for all the built-ins and fixes the portal bug.

[Commit to
review](https://github.com/facebook/react/pull/32640/commits/e068c167d48d4df01e79db8f13276bb46d7ab439)

DiffTrain build for [8243f3f063](https://github.com/facebook/react/commit/8243f3f0631698e819c690710a7f18f767068981)
2025-03-17 08:30:28 -07:00
rickhanlonii 58d48f556d Remove offscreen type (#32639)
Based off https://github.com/facebook/react/pull/32499

This is no longer used.

[Review
commit](https://github.com/facebook/react/commit/88c297d12f8b2562be3982fba867f03a137551cb)

DiffTrain build for [df31952275](https://github.com/facebook/react/commit/df319522758b7fdfed3ddfa517cc1cc298ef1602)
2025-03-17 06:44:18 -07:00
rickhanlonii e4b2c5b29a [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.

DiffTrain build for [1a191701fe](https://github.com/facebook/react/commit/1a191701fe5000098d23328b2ea9d70457fea1f8)
2025-03-17 06:27:19 -07:00
sebmarkbage 025e752e40 Add more phases to the ReactFiberApplyGesture (#32578)
Stacked on #32585 and #32605.

This adds more loops for the phases of "Apply Gesture". It doesn't
implement the interesting bit yet like adding view-transition-names and
measurements. I'll do that in a separate PR to keep reviewing easier.

The three phases of this approach is roughly:

- Clone and apply names to the "old" state.
- Inside startViewTransition: Apply names to the "new" state. Measure
both the "old" and "new" state to know whether to cancel some of them.
Delete the clones which will include all the "old" names.
- After startViewTransition: Restore "new" names back to no
view-transition-name.

Since we don't have any other Effects in these phases we have a bit more
flexibility and we can avoid extra phases that traverse the tree. I've
tried to avoid any additional passes.

An interesting consequence of this approach is that we could measure
both the "old" and "new" state before `startViewTransition`. This would
be more efficient because we wouldn't need to take View Transition
snapshots of parts of the tree that won't actually animate. However,
that would require an extra pass and force layout earlier. It would also
have different semantics from the fire-and-forget View Transitions
because we could optimize better which can be visible. It would also not
account for any late mutations. So I decided to instead let the layout
be computed by painting as usual and then measure both "old" and "new"
inside the startViewTransition instead. Then canceling anything that
doesn't animate to keep it consistent.

Unfortunately, though there's not a lot of code sharing possible in
these phases because the strategy is so different with the cloning and
because the animation is performed in reverse. The "finishedWork" Fiber
represents the "old" state and the "current" Fiber represents the "new"
state.

The most complicated phase is the cloning. I actually ended up having to
make a very different pattern from the other phases and CommitWork in
general. Because we have to clone as we go and also do other things like
apply names and finding pairs, it has more phases. I ended up with an
approach that uses three different loops. The outer one for updated
trees, one for inserted trees that don't need cloning (doesn't include
reappearing offscreen) and one for not updated trees that still need
cloning. Inside each loop it can also be in different phases which I
track with the `visitPhase` enum - this pattern is kind of new.

Additionally, we need to measure the cloned nodes after we've applied
mutations to them and we have to wait until the whole tree is inserted.
We don't have a reference to these DOM elements in the Fiber tree since
that still refers to the original ones. We need to store the cloned
elements somewhere. So I added a temporary field on the
ViewTransitionState to keep track of any clones owned by that
ViewTransition.

When we deep clone an unchanged subtree we don't have DOM element
instances. It wouldn't be quite safe to try to find them from the tree
structure. So we need to avoid the deep clones if we might need DOM
elements. Therefore we keep traversing in the case where we need to find
nested ViewTransition boundaries that are either potentially affected by
layout or a "pair".

For the other two phases the pattern there's a lot of code duplication
since it's slightly different from the commit ones but they at least
follow the same pattern. For the restore phase I was actually able to
reuse most of the code.

I don't love how much code this is.

DiffTrain build for [c4a3b92e09](https://github.com/facebook/react/commit/c4a3b92e098cf1896939758e7419cbdb0e2f0cf4)
2025-03-14 10:28:49 -07:00
sebmarkbage 58c68bafca Gate flushGestureMutations and flushGestureAnimations (#32605)
Normally these are gated by the whole commitGestureOnRoot path but in
the case of an early commit these phases may need to be invoked.
Earlier. Those paths weren't gated which I noticed when I started adding
code to them.

DiffTrain build for [3e956805e8](https://github.com/facebook/react/commit/3e956805e899bff7aea7b19c56e6adaf362cdc2b)
2025-03-14 10:27:08 -07:00
poteto acc3720f55 feat(eslint-plugin-react-hooks): merge rule from eslint-plugin-react-compiler into react-hooks plugin (#32416)
This change merges the `react-compiler` rule from
`eslint-plugin-react-compiler` into the `eslint-plugin-react-hooks`
plugin. In order to do the move in a way that keeps commit history with
the moved files, but also no remove them from their origin until a
future cleanup change can be done, I did the `git mv` first, and then
recreated the files that were moved in their original places, as a
separate commit. Unfortunately GH shows the moved files as new instead
of the ones that are truly new. But in the IDE and `git blame`, commit
history is intact with the moved files.

Since this change adds new dependencies, and one of those dependencies
has a higher `engines` declaration for `node` than what the plugin
currently has, this is technically a breaking change and will have to go
out as part of a major release.

### Related Changes
- https://github.com/facebook/react/pull/32458

---------

Co-authored-by: Lauren Tan <poteto@users.noreply.github.com>

DiffTrain build for [5ccfcd17ff](https://github.com/facebook/react/commit/5ccfcd17ffa0adf9e7f5ba7fbf48e6bf6a4eb67e)
2025-03-12 18:53:50 -07:00
poteto 0f058a31fe Update babel configs used in jest (#32588)
Extracting portions of #32416 for easier review.

This PR updates our babel configs (only used in jest) to support
classes.

Co-authored-by: michael faith <michaelfaith@users.noreply.github.com>
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32588).
* #32592
* #32591
* #32590
* #32589
* __->__ #32588

Co-authored-by: michael faith <michaelfaith@users.noreply.github.com>

DiffTrain build for [f695f95290](https://github.com/facebook/react/commit/f695f95290aa3560a00e8a3b617205ac9e087e0e)
2025-03-12 16:18:21 -07:00
jackpope 0f346a4282 Add ref to Fragment (#32465)
*This API is experimental and subject to change or removal.*

This PR is an alternative to
https://github.com/facebook/react/pull/32421 based on feedback:
https://github.com/facebook/react/pull/32421#pullrequestreview-2625382015
. The difference here is that we traverse from the Fragment's fiber at
operation time instead of keeping a set of children on the
`FragmentInstance`. We still need to handle newly added or removed child
nodes to apply event listeners and observers, so we treat those updates
as effects.

**Fragment Refs**

This PR extends React's Fragment component to accept a `ref` prop. The
Fragment's ref will attach to a custom host instance, which will provide
an Element-like API for working with the Fragment's host parent and host
children.

Here I've implemented `addEventListener`, `removeEventListener`, and
`focus` to get started but we'll be iterating on this by adding
additional APIs in future PRs. This sets up the mechanism to attach refs
and perform operations on children. The FragmentInstance is implemented
in `react-dom` here but is planned for Fabric as well.

The API works by targeting the first level of host children and proxying
Element-like APIs to allow developers to manage groups of elements or
elements that cannot be easily accessed such as from a third-party
library or deep in a tree of Functional Component wrappers.

```javascript
import {Fragment, useRef} from 'react';

const fragmentRef = useRef(null);

<Fragment ref={fragmentRef}>
  <div id="A" />
  <Wrapper>
    <div id="B">
      <div id="C" />
    </div>
  </Wrapper>
  <div id="D" />
</Fragment>
```

In this case, calling `fragmentRef.current.addEventListener()` would
apply an event listener to `A`, `B`, and `D`. `C` is skipped because it
is nested under the first level of Host Component. If another Host
Component was appended as a sibling to `A`, `B`, or `D`, the event
listener would be applied to that element as well and any other APIs
would also affect the newly added child.

This is an implementation of the basic feature as a starting point for
feedback and further iteration.

DiffTrain build for [6aa8254bb7](https://github.com/facebook/react/commit/6aa8254bb7353fe3096289edc669cf168e9fd190)
2025-03-12 07:38:22 -07:00
sebmarkbage dd554f3547 Check if a child is a new child before calling moveBefore (#32567)
This fixes a critical issue with moveBefore. I was told that the
disconnected -> connected case was going to be relaxed and not be an
error but apparently that is not the case.

This means that we can't use this for initial insertions. Only moves.

Unfortunately React's internals doesn't distinguish these cases. This
adds a hack that checks each nodes but this is pretty bad for
performance. We should only call this in one or the other case.

Given that we still need feature detection. Both of which means that
these calls are no longer inlined and this extra code. I wonder if it's
even worth it given that you can't even rely on it working anyway since
not all browsers have it. Kind of don't want to ship this until all
browsers have it.

Even then we'd ideally refactor React to use separate code paths for
initial insertion vs moves. Which leads to some unfortunate code
duplication.

DiffTrain build for [99e1024051](https://github.com/facebook/react/commit/99e1024051f2e6b2d2849b966e2f4354aef2a1d0)
2025-03-10 15:18:48 -07:00
jackpope 64aba73cf4 Make renameElementSymbol dynamic for native fb (#32566)
Use variant to begin rolling this out internally.

DiffTrain build for [50ab2dde94](https://github.com/facebook/react/commit/50ab2dde940bf0027773a944da005277b3d5598a)
2025-03-10 12:30:09 -07:00
sammy-SC ffe288a749 Fix asserts caused by OffscreenComponent rendering in React Native with passChildrenWhenCloningPersistedNodes (#32528)
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

This PR fixes asserts when `passChildrenWhenCloningPersistedNodes` is
enabled for React Native and OffscreenComponent child rendering unhides
host components.

Discussions around possible fixes for the asserts seen in React Native
suggested changing the way we handle hiding/unhiding host components by
updating the fiber state with the hidden host component instead of
submitting a hidden clone Fabric and keeping the original as the current
fiber.

Implementing this fix would require holding onto the original styling of
the hidden host component. The reconciler updates the styling by adding
`display: none` to hide the contents. If the original host component was
already hidden, the renderer would lose that information and remove the
styling when showing the contents again.

To reduce the changes required to make
`passChildrenWhenCloningPersistedNodes` work, this PR falls back to the
original cloning method when OffscreenComponents are part of the
children needed to be added back. This effectively resolve the asserts
triggered by the feature in RN and improves overall performance.

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->

This fix was tested by enabling `passChildrenWhenCloningPersistedNodes`
in an app built with React Native that had a repro for triggering the
asserts. The asserts do not occur anymore when using the changes in this
PR.

---------

Co-authored-by: Nick <lefever@meta.com>

DiffTrain build for [cc680065c3](https://github.com/facebook/react/commit/cc680065c33739cc4c8cd2e8a67312b0c16a6ccc)
2025-03-07 09:09:51 -08:00
eps1lon 5fa669315f Add Owner Stack to attribute hydration mismatches (#32538)
DiffTrain build for [029e8bd618](https://github.com/facebook/react/commit/029e8bd618af23fbdd9efdac565ad81f7d4640d8)
2025-03-06 08:18:17 -08:00
eps1lon 4f50377ac2 Support beforetoggle/toggle events for dialog (#32479)
DiffTrain build for [aac177c484](https://github.com/facebook/react/commit/aac177c48439ab294f72e8b5a85059daa3f8a5ee)
2025-03-05 10:51:01 -08:00
sebmarkbage 7020910640 During a Swipe Gesture Render a Clone Offscreen and Animate it Onscreen (#32500)
This is really the essence mechanism of the `useSwipeTransition`
feature.

We don't want to immediately switch to the destination state when
starting a gesture. The effects remain mounted on the current state. We
want the current state to be "live". This is important to for example
allow a video to keeping playing while starting a swipe (think
TikTok/Reels) and not stop until you've committed the action. The only
thing that can be live is the "new" state. Therefore we treat the
destination as the "old" state and perform a reverse animation from
there.

Ideally we could apply the old state to the DOM tree, take a snapshot
and then revert it back in the mutation of `startViewTransition`.
Unfortunately, the way `startViewTransition` was designed it always
paints one frame of the "old" state which would lead this to cause a
flicker.

To work around this, we need to create a clone of any View Transition
boundary that might be mutated and then render that offscreen. That way
we can render the "current" state on screen and the "destination" state
offscreen for the screenshots. Being mutated can be either due to React
doing a DOM mutation or if a child boundary resizes that causes the
parent to relayout. We don't have to do this for insertions or deletions
since they only appear on one side.

The worst case scenario is that we have to clone the whole root. That's
what this first PR implements. We clone the container and if it's not
absolutely positioned, we position it on top of the current one. If the
container is `document` or `<html>` we instead clone the `<body>` tag
since it's the only one we can insert a duplicate of. If the container
is deep in the tree we clone just that even though technically we should
probably clone the whole document in that case. We just keep the impact
smaller. Ideally though we'd never hit this case. In fact, if we clone
the document we issue a warning (always for now) since you probably
should optimize this. In the future I intend to add optimizations when
affected View Transition boundaries are absolutely positioned since they
cannot possibly relayout the parent. This would be the ideal way to use
this feature most efficiently but it still works without it.

Since we render the "old" state outside the viewport, we need to then
adjust the animation to put it back into the viewport. This is the
trickiest part to get right while still preserving any customization of
the View Transitions done using CSS. This current approach reapplies all
the animations with adjusted keyframes.

In the case of an "exit" the pseudo-element itself is positioned outside
the viewport but since we can't programmatically update the style of the
pseudo-element itself we instead adjust all the keyframes to put it back
into the viewport. If there is no animation on the group we add one.

In the case of an "update" the pseudo-element is positioned on the new
state which is already inside the viewport. However, the auto-generated
animation of the group has a starting keyframe that starts outside the
viewport. In this case we need to adjust that keyframe.

In the future I might explore a technique that inserts stylesheets
instead of mutating the animations. It might be simpler. But whatever
hacks work to maximize the compatibility is best.

DiffTrain build for [e9252bcdcc](https://github.com/facebook/react/commit/e9252bcdccf7f8f691081e4d48ca47657bc723f9)
2025-03-04 17:15:30 -08:00
rickhanlonii c2530474d3 [flags] remove enableOwnerStacks (#32426)
Bassed off: https://github.com/facebook/react/pull/32425

Wait to land internally.

[Commit to
review.](https://github.com/facebook/react/pull/32426/commits/66aa6a4dbb78106b4f3d3eb367f5c27eb8f30c66)

This has landed everywhere

DiffTrain build for [e0fe347967](https://github.com/facebook/react/commit/e0fe3479671555e01531dbc3d2fd85d5bd4c5a56)
2025-03-04 09:40:20 -08:00
sebmarkbage 0fc4c58f7c Polyfill onScrollEnd Event in Safari (#32427)
We added support for `onScrollEnd` in #26789 but it only works in Chrome
and Firefox. Safari still doesn't support `scrollend` and there's no
indication that they will anytime soon so this polyfills it.

While I don't particularly love our synthetic event system this tries to
stay within the realm of how our other polyfills work. This implements
all `onScrollEnd` events as a plugin.

The basic principle is to first feature detect the `onscrollend` DOM
property to see if there's native support and otherwise just use the
native event.

Then we listen to `scroll` events and set a timeout. If we don't get any
more scroll events before the timeout we fire `onScrollEnd`. Basically
debouncing it. If we're currently pressing down on touch or a mouse then
we wait until it is lifted such as if you're scrolling with a finger or
using the scrollbars on desktop but isn't currently moving.

If we do get any native events even though we're in polyfilling mode, we
use that as an indication to fire the `onScrollEnd` early.

Part of the motivation is that this becomes extra useful pair for
https://github.com/facebook/react/pull/32422. We also probably need
these events to coincide with other gesture related internals so you're
better off using our polyfill so they're synced.

DiffTrain build for [605a880c8c](https://github.com/facebook/react/commit/605a880c8c5191e9f8c52468458709cd17a486c1)
2025-03-03 11:30:13 -08:00
sebmarkbage 0f71e700f0 Add Commit Scaffolding for Gestures (#32451)
This adds a `ReactFiberApplyGesture` which is basically intended to be a
fork of the phases in `ReactFiberCommitWork` except for the fake commit
that `useSwipeTransition` does. So far none of the phases are actually
implemented yet. This is just the scaffolding around them so I can fill
them in later.

The important bit is that we call `startViewTransition` (via the
`startGestureTransition` Config) when a gesture starts. We add a paused
animation to prevent the transition from committing (even if the
ScrollTimeline goes to 100%). This also locks the documents so that we
can't commit any other Transitions until it completes.

When the gesture completes (scroll end) then we stop the gesture View
Transition. If there's no new work scheduled we do that immediately but
if there was any new work already scheduled, then we assume that this
will potentially commit the new state. So we wait for that to finish.
This lets us lock the animation in its state instead of snapping back
and then applying the real update.

Using this technique we can't actually run a View Transition from the
current state to the actual committed state because it would snap back
to the beginning and then run the View Transition from there. Therefore
any new commit needs to skip View Transitions even if it should've
technically animated to that state. We assume that the new state is the
same as the optimistic state you already swiped to. An alternative to
this technique could be to commit the optimistic state when we cancel
and then apply any new updates o top of that. I might explore that in
the future.

Regardless it's important that the `action` associated with the swipe
schedules some work before we cancel. Otherwise it risks reverting
first. So I had to update this in the fixture.

DiffTrain build for [3607f4838a](https://github.com/facebook/react/commit/3607f4838a8f4a87160da36aa26bb1432d7a5f11)
2025-02-27 13:50:46 -08:00