This implements `getRootNode(options)` on fragment instances as the
equivalent of calling `getRootNode` on the fragment's parent host node.
The parent host instance will also be used to proxy dispatchEvent in an
upcoming PR.
Adds `getClientRects()` to fragment instances with a fixture test case.
`Element.getClientRect` returns a collection of `DOMRect`s (see example
of multiline span returning two `DOMRect` boxes).
`fragmentInstance.getClientRects` here flattens those collections into
an array of rects.
`focus()` was added in https://github.com/facebook/react/pull/32465.
Here we add `focusLast()` and `blur()`. I also extended `focus` to take
options.
`focus` will focus the first focusable element. `focusLast` will focus
the last focusable element. We could consider a `focusFirst` naming or
even the `focusWithin` used by test selector APIs as well.
`blur` will only have an effect if the current `document.activeElement`
is one of the fragment children.
This implements `observeUsing(observer)` and `unobserverUsing(observer)`
on fragment instances. IntersectionObservers and ResizeObservers can be
passed to observe each host child of the fragment. This is the
equivalent to calling `observer.observe(child)` or
`observer.unobserve(child)` for each child target.
Just like the addEventListener, the observer is held on the fragment
instance and applied to any newly mounted child. So you can do things
like wrap a paginated list in a fragment and have each child
automatically observed as they commit in.
Unlike, the event listeners though, we don't `unobserve` when a child is
removed. If a removed child is currently intersecting, the observer
callback will be called when it is removed with an empty rect. This lets
you track all the currently intersecting elements by setting state from
the observer callback and either adding or removing them from your list
depending on the intersecting state. If you want to track the removal of
items offscreen, you'd have to maintain that state separately and append
intersecting data to it in the observer callback. This is what the
fixture example does.
There could be more convenient ways of managing the state of multiple
child intersections, but basic examples are able to be modeled with the
simple implementation. Let's see how the usage goes as we integrate this
with more advanced loggers and other features.
For now you can only attach one observer to an instance. This could
change based on usage but the fragments are composable and could be
stacked as one way to apply multiple observers to the same elements.
In practice, one pattern we expect to enable is more composable logging
such as
```javascript
function Feed({ items }) {
return (
<ImpressionLogger>
{items.map((item) => (
<FeedItem />
))}
</ImpressionLogger>
);
}
```
where `ImpressionLogger` would set up the IntersectionObserver using a
fragment ref with the required business logic and various components
could layer it wherever the logging is needed. Currently most callsites
use a hook form, which can require wiring up refs through the tree and
merging refs for multiple loggers.
*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.
For the `useId` algorithm we used colon `:` before and after.
https://github.com/facebook/react/pull/23360
This avoids collisions in general by using an unusual characters. It
also avoids collisions when concatenated with some other ID.
Unfortunately, `:` is not a valid character in `view-transition-name`.
This PR swaps the format from:
```
:r123:
```
To the unicode:
```
«r123»
```
Which is valid CSS selectors. This also allows them being used for
`querySelector()` which we didn't really find a legit use for but seems
ok-ish.
That way you can get a view-transition-name that you can manually
reference. E.g. to generate styles:
```js
const id = useId();
return <>
<style>{`
::view-transition-group(${id}) { ... }
::view-transition-old(${id}) { ... }
::view-transition-new(${id}) { ... }
`}</style>
<ViewTransition name={id}>...</ViewTransition>
</>;
```
Link headers are optionally supported for cases where you prefer to send
resource loading hints before you're ready to send the body of a
request. While many resources can be correctly preloaded from a link
header responsive images are currently not supported and end up
preloading the default src rather than the correctly sized image. Until
responsive images are supported React will not allow these images to
preload as headers and will retain them to preload as HTML.
closes: #32437
Merges the useResourceEffect API into useEffect while keeping the
underlying implementation the same. useResourceEffect will be removed in
the next diff.
To fork between behavior we rely on a `typeof` check for the updater or
destroy function in addition to the CRUD feature flag. This does now
have to be checked every time (instead of inlined statically like before
due to them being different hooks) which will incur some non-zero amount
(possibly negligble) of overhead for every effect.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32205).
* #32206
* __->__ #32205
followup to
* https://github.com/facebook/react/pull/32069
* https://github.com/facebook/react/pull/32163
* https://github.com/facebook/react/pull/32224
in react-dom in Dev we validate that the tag nesting is valid. This is
motivated primarily because while browsers are tolerant to poor HTML
there are many cases that if server rendered will be hydrated in a way
that will break hydration.
With the changes to singleton scoping where the document body is now the
implicit render/hydration context for arbitrary tags at the root we need
to adjust the validation logic to allow for valid programs such as
rendering divs as a child of a Document (since this div will actually
insert into the body).
3 years ago we partially disabled comment nodes as valid containers.
Some unflagged support was left in due to legacy APIs like
`unmountComponentAtNode` and `unstable_renderSubtreeIntoContainer` but
these were since removed in React 19. This update flags the remaining
uses of comments as containers.
follow up to https://github.com/facebook/react/pull/32163
This continues the work of making Suspense workable anywhere in a
react-dom tree. See the prior PRs for how we handle server rendering and
client rendering. In this change we update the hydration implementation
to be able to locate expected nodes. In particular this means hydration
understands now that the default hydration context is the document body
when the container is above the body.
One case that is unique to hydration is clearing Suspense boundaries.
When hydration fails or when the server instructs the client to recover
an errored boundary it's possible that the html, head, and body tags in
the initial document were written from a fallback or a different primary
content on the server and need to be replaced by the client render.
However these tags (and in the case of head, their content) won't be
inside the comment nodes that identify the bounds of the Suspense
boundary. And when client rendering you may not even render the same
singletons that were server rendered. So when server rendering a
boudnary which contributes to the preamble (the html, head, and body tag
openings plus the head contents) we emit a special marker comment just
before closing the boundary out. This marker encodes which parts of the
preamble this boundary owned. If we need to clear the suspense boundary
on the client we read this marker and use it to reset the appropriate
singleton state.
This is a follow up to https://github.com/facebook/react/pull/32069
In the prior change I updated Fizz to allow you to render Suspense
boundaries at any level within a react-dom application by treating the
document body as the default render scope. This change updates Fiber to
provide similar semantics. Note that this update still does not deliver
hydration so unifying the Fizz and Fiber implementations in a single App
is not possible yet.
The implementation required a rework of the getHostSibling and
getHostParent algorithms. Now most HostSingletons are invisible from a
host positioning perspective. Head is special in that it is a valid host
scope so when you have Placements inside of it, it will act as the
parent. But body, and html, will not directly participate in host
positioning.
Additionally to support flipping to a fallback html, head, and body tag
in a Suspense fallback I updated the offscreen hiding/unhide logic to
pierce through singletons when lookin for matching hidable nod
boundaries anywhere (excluding hydration)
Suspense is meant to be composable but there has been a lonstanding
limitation with using Suspense above the `<body>` tag of an HTML
document due to peculiarities of how HTML is parsed. For instance if you
used Suspense to render an entire HTML document and had a fallback that
might flush an alternate Document the comment nodes which describe this
boundary scope won't be where they need to be in the DOM for client
React to properly hydrate them. This is somewhat a problem of our own
making in that we have a concept of a Preamble and we leave the closing
body and html tags behind until streaming has completed which produces a
valid HTML document that also matches the DOM structure that would be
parsed from it. However Preambles as a concept are too important to
features like Float to imagine moving away from this model and so we can
either choose to just accept that you cannot use Suspense anywhere
except inside the `<body>` or we can build special support for Suspense
into react-dom that has a coherent semantic with how HTML documents are
written and parsed.
This change implements Suspense support for react-dom/server by
correctly serializing boundaries during rendering, prerendering, and
resumgin on the server. It does not yet support Suspense everywhere on
the client but this will arrive in a subsequent change. In practice
Suspense cannot be used above the `<body>` tag today so this is not a
breaking change since no programs in the wild could be using this
feature anyway.
React's streaming rendering of HTML doesn't lend itself to replacing the
contents of the documentElement, head, or body of a Document. These are
already special cased in fiber as HostSingletons and similarly for Fizz
the values we render for these tags must never be updated by the Fizz
runtime once written. To accomplish these we redefine the Preamble as
the tags that represent these three singletons plus the contents of the
document.head. If you use Suspense above any part of the Preamble then
nothing will be written to the destination until the boundary is no
longer pending. If the boundary completes then the preamble from within
that boudnary will be output. If the boundary postpones or errors then
the preamble from the fallback will be used instead.
Additionally, by default anything that is not part of the preamble is
implicitly in body scope. This leads to the somewhat counterintuitive
consequence that the comment nodes we use to mark the borders of a
Suspense boundary in Fizz can appear INSIDE the preamble that was
rendered within it.
```typescript
render((
<Suspense>
<html lang="en">
<body>
<div>hello world</div>
</body>
</html>
</Suspense>
))
```
will produce an HTML document like this
```html
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<!--$--> <-- this is the comment Node representing the outermost Suspense
<div>hello world</div>
<$--/$-->
</body>
</html>
```
Later when I update Fiber to support Suspense anywhere hydration will
similarly start implicitly in the document body when the root is part of
the preamble (the document or one of it's singletons).
The public API has been deleted a long time ago so this should be unused
unless it's used by hacks. It should be replaced with an
effect/lifecycle that manually tracks this if you need it.
The problem with this API is how the timing implemented because it
requires Placement/Hydration flags to be cleared too early. In fact,
that's why we also have a separate PlacementDEV flag that works
differently.
https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberCommitWork.js#L2157-L2165
We should be able to remove this code now.
This treats workInProgressRoot work and rootWithPendingPassiveEffects
the same way. Basically as long as there's some work on the root, yield
the current task. Including passive effects. This means that passive
effects are now a continuation instead of a separate callback. This can
mean they're earlier or later than before. Later for Idle in case
there's other non-React work. Earlier for same Default if there's other
Default priority work.
This makes sense since increasing priority of the passive effects beyond
Idle doesn't really make sense for an Idle render.
However, for any given render at same priority it's more important to
complete this work than start something new.
Since we special case continuations to always yield to the browser, this
has the same effect as #31784 without implementing `requestPaint`. At
least assuming nothing else calls `requestPaint`.
<img width="587" alt="Screenshot 2024-12-14 at 5 37 37 PM"
src="https://github.com/user-attachments/assets/8641b172-8842-4191-8bf0-50cbe263a30c"
/>
We're seeing errors when testing useResourceEffect in SSR and it turns
out we're missing the noop dispatcher function on Fizz.
I tested a local build with this change and it resolved the late
mutation errors in the e2e tests.
When scheduling the initial root and when using
`unstable_scheduleHydration` we should use the Hydration Lanes rather
than the raw update lane. This ensures that we're always hydrating using
a Hydration Lane or the Offscreen Lane rather than other lanes getting
some random hydration in it.
This fixes an issue where updating a root while it is still hydrating
causes it to trigger client rendering when it could just hydrate and
then apply the update on top of that.
It also fixes a potential performance issue where
`unstable_scheduleHydration` gets batched with an update that then ends
up forcing an update of a boundary that requires it to rewind to do the
hydration lane anyway. Might as well just start with the hydration
without the update applied first.
I added a kill switch (`enableHydrationLaneScheduling`) just in case but
seems very safe given that using `unstable_scheduleHydration` at all is
very rare and updating the root before the shell hydrates is extremely
rare (and used to trigger a recoverable error).
When streaming SSR while hydrating React will wait for Suspense
boundaries to be revealed by the SSR stream before attempting to hydrate
them. The rationale here is that the Server render is likely further
ahead of whatever the client would produce so waiting to let the server
stream in the UI is preferable to retrying on the client and possibly
delaying how quickly the primary content becomes available. However If
the connection closes early (user hits stop for instance) or there is a
server error which prevents additional HTML from being delivered to the
client this can put React into a broken state where the boundary never
resolves nor errors and the hydration never retries that boundary
freezing it in it's fallback state.
Once the document has fully loaded we know there is not way any
additional Suspense boundaries can arrive. This update changes react-dom
on the client to schedule client renders for any unfinished Suspense
boundaries upon document loading.
The technique for client rendering a fallback is pretty straight
forward. When hydrating a Suspense boundary if the Document is in
'complete' readyState we interpret pending boundaries as fallback
boundaries. If the readyState is not 'complete' we register an event to
retry the boundary when the DOMContentLoaded event fires.
To test this I needed JSDOM to model readyState. We previously had a
temporary implementation of readyState for SSR streaming but I ended up
implementing this as a mock of JSDOM that implements a fake readyState
that is mutable. It starts off in 'loading' readyState and you can
advance it by mutating document.readyState. You can also reset it to
'loading'. It fires events when changing states.
This seems like the least invasive way to get closer-to-real-browser
behavior in a way that won't require remembering this subtle detail
every time you create a test that asserts Suspense resolution order.
## Summary
This fixes a typo in the error that gets reported when Float errors
while hoisting a style tag that does not contain both `precedence` and
`href`. There was a typo in _conflict_ and the last part of the sentence
doesn't make sense. I assume it wasn't needed since the message already
suggests moving the style tag to the head manually.
We've previously failed to land this change due to some internal apps
seeing infinite render loops due to external store state updates during
render. It turns out that since the `renderWasConcurrent` var was moved
into the do block, the sync render triggered from the external store
check was stuck with a `RootSuspended` `exitStatus`. So this is not
unique to sibling prerendering but more generally related to how we
handle update to a sync external store during render.
We've tested this build against local repros which now render without
crashes. We will try to add a unit test to cover the scenario as well.
---------
Co-authored-by: Andrew Clark <git@andrewclark.io>
Co-authored-by: Rick Hanlon <rickhanlonii@fb.com>
This reverts commit 6c4bbc7832.
It looked like the bug we found on the original land was related to
broken product code. But through landing #31268 we found additional bugs
internally. Since disabling the feature flag does not fix the bugs, we
have to revert again to unblock the sync. We can continue to debug with
our internal build.
When we added `renderToReadableStream` we added the `allReady` helper to
make it easier to do SSG rendering but it's kind of awkward to wire up
that way. Since we're also discouraging `renderToString` in React 19 the
cliff is kind of awkward. ([As noted by
Docusaurus.](https://github.com/facebook/react/pull/24752#issuecomment-2178309299))
The idea of the `react-dom/static` `prerender` API was that this would
be the replacement for SSG rendering. Awkwardly this entry point
actually already exists in stable but it has only `undefined` exports.
Since then we've also added other useful heuristics into the `prerender`
branch that makes this really the favored and easiest to use API for the
prerender (SSG/ISR) use case.
`prerender` is also used for Partial Prerendering but that part is still
experimental.
However, we can expose only the `prerender` API on `react-dom/static`
without it returning the `postponeState`. Instead the stream is on
`prelude`. The naming is a bit awkward if you don't consider resuming
but it's the same thing.
It's really just `renderToReadable` stream with automatic `allReady` and
better heuristics for prerendering.
Follows https://github.com/facebook/react/pull/31238
___
This is a partial re-land of
https://github.com/facebook/react/pull/31056. We saw breakages surface
after the original land and had to revert. Now that they've been fixed,
let's try this again. This time we'll split up the commits to give us
more control of testing and rollout internally.
Original PR: https://github.com/facebook/react/pull/31056
Original Commit:
4c71025d8d
Revert PR: https://github.com/facebook/react/pull/31080
Commit description:
> When a synchronous update suspends, and we prerender the siblings, the
prerendering should be non-blocking so that we can immediately restart
once the data arrives.
>
> This happens automatically when there's a Suspense boundary, because
we immediately commit the boundary and then proceed to a Retry render,
which are always concurrent. When there's not a Suspense boundary, there
is no Retry, so we need to take care to switch from the synchronous work
loop to the concurrent one, to enable time slicing.
Co-authored-by: Andrew Clark <git@andrewclark.io>