This clarifies a few things by ensuring that there is always at least
one required field. This can be used to refine the object to one of the
specific types. However, it's probably just a matter of time until we
make this tagged unions instead. E.g. it would be nice to rename the
`name` field `ReactComponentInfo` to `type` and tag it with the React
Element symbol because then it's just the same as a React Element.
I also extract a time field. The idea is that this will advance (or
rewind) the time to the new timestamp and then anything below would be
defined as happening within that time stamp. E.g. to model the start and
end for a server component you'd do something like:
```
[
{time: 123},
{name: 'Component', ... },
{time: 124},
]
```
The reason this needs to be in the `ReactDebugInfo` is so that timing
information from one environment gets transferred into the next
environment. It lets you take a Promise from one world and transfer it
into another world and its timing information is preserved without
everything else being preserved.
I've gone back and forth on if this should be part of each other Info
object like `ReactComponentInfo` but since those can be deduped and can
change formats (e.g. this should really just be a React Element) it's
better to store this separately.
The time format is relative to a `timeOrigin` which is the current
environment's `timeOrigin`. When it's serialized between environments
this needs to be considered.
Emitting these timings is not yet implemented in this PR.
---------
Co-authored-by: eps1lon <sebastian.silbermann@vercel.com>
This is just moving some code into a helper.
We have a bunch of special cases for the return value slot of a Server
Component that's different from just rendering that inside an object.
This was getting a little tricky to reason about inline with the rest of
rendering.
Hints and Console logs are side-effects and don't belong to any
particular value. They're `void`. Therefore they don't need a row ID.
In the current parsing scheme it's ok to omit the id. It just becomes
`0` which is the initial value which is then unused for these row types.
So it looks like:
```
:HP[...]
:W[...]
0:{...}
```
We could patch the parsing to encode the tag in the ID so it's more like
the ID is the target of the side-effect.
```
H:P[...]
W:[...]
0:{...}
```
Or move the tagging to the beginning like it used to be.
But this seems simple enough for now.
To avoid GC pressure and accidentally hanging onto old trees Suspense
boundary retries are now implemented in the commit phase. I used the
Callback flag which was previously only used to schedule callbacks for
Class components. This isn't quite semantically equivalent but it's
unused and seemingly compatible.
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.
## Overview
Changes the error message to say "Server Functions" instead of "Server
Actions" since this error can fire in cases like:
```
<button onClick={serverFunction} />
```
Which is calling a server function, not a server action.
A long standing issue for React has been that if you reorder stateful
nodes, they may lose their state and reload. The thing moving loses its
state. There's no way to solve this in general where two stateful nodes
swap.
The [`moveBefore()`
proposal](https://chromestatus.com/feature/5135990159835136?gate=5177450351558656)
has now moved to
[intent-to-ship](https://groups.google.com/a/chromium.org/g/blink-dev/c/YE_xLH6MkRs/m/_7CD0NYMAAAJ).
This function is kind of like `insertBefore` but preserves state.
There's [a demo here](https://state-preserving-atomic-move.glitch.me/).
Ideally we'd port this demo to a fixture so we can try it.
Currently this flag is always off - even in experimental. That's because
this is still behind a Chrome flag so it's a little early to turn it on
even in experimental. So you need a custom build. It's on in RN but only
because it doesn't apply there which makes it easier to tell that it's
safe to ship once it's on everywhere else.
The other reason it's still off is because there's currently a semantic
breaking change. `moveBefore()` errors if both nodes are disconnected.
That happens if we're inside a completely disconnected React root.
That's not usually how you should use React because it means effects
can't read layout etc. However, it is currently supported. To handle
this we'd have to try/catch the `moveBefore` to handle this case but we
hope this semantic will change before it ships. Before we turn this on
in experimental we either have to wait for the implementation to not
error in the disconnected-disconnected case in Chrome or we'd have to
add try/catch.
This is a hack that ensures that all four lanes as visible whether you
have any tracks in them or not, and that they're in the priority order
within the Scheduler track group. We do want to show all even if they're
not used because it shows what options you're missing out on.
<img width="1035" alt="Screenshot 2024-11-22 at 12 38 30 PM"
src="https://github.com/user-attachments/assets/f30ab0b9-af5e-48ed-b042-138444352575">
In Chrome, the order of tracks within a group are determined by the
earliest start time. We add fake markers at start time zero in that
order eagerly. Ideally we could do this only once but because calls that
aren't recorded aren't considered for ordering purposes, we need to keep
adding these over and over again in case recording has just started. We
can't tell when recording starts.
Currently performance.mark() are in first insertion order but
performance.measure() are in the reverse order. I'm not sure that's
intentional. We can always add the 0 time slot even if it's in the past.
That's still considered for ordering purposes as long as the measurement
is recorded at the time we call it.
This ensures that we mark the time from ping until we render as
"Blocked".
We intentionally don't want to show the event time even if it's
something like "load" because it draws attention away from interactions
etc.
<img width="577" alt="Screenshot 2024-11-21 at 7 22 39 PM"
src="https://github.com/user-attachments/assets/70cca2e8-bd5e-489f-98f0-b4dfee5940af">
This avoid re-emitting the yellow "Event" log when we ping inside the
original event. Instead of treating events as repeated when we get
repeated updates, we treat them as repeated if we've ever logged out
this event before.
Additionally, in the case the prerender sibling flag is on we need to
ensure that if a render gets interrupted when it has been suspended we
treat that as "Prewarm" instead of "Interrupted Render".
Before:
<img width="539" alt="Screenshot 2024-11-19 at 2 39 44 PM"
src="https://github.com/user-attachments/assets/190ca50c-5168-40d8-a6fd-6b9a583af1f0">
After:
<img width="1004" alt="Screenshot 2024-11-21 at 4 53 16 PM"
src="https://github.com/user-attachments/assets/0c441ada-1ed1-412c-8935-aaf040c25dfe">
Fixes a bug with the experimental `useResourceEffect` hook where we
would compare the wrong deps when there happened to be another kind of
effect preceding the ResourceEffect. To do this correctly we need to add
a pointer to the ResourceEffect's identity on the update.
I also unified the previously separate push effect impls for resource
effects since they are always pushed together as a unit.
Stacked on #31552. Must be tested with `enableSiblingPrerendering` off
since the `use()` optimization is not on there yet.
This adds a span to the Components track when we yield in the middle of
the event loop. In this scenario, the "Render" span continues through
out the Scheduler track. So you can see that the Component itself might
not take a long time but yielding inside of it might.
This lets you see if something was blocking the React render loop while
yielding. If we're blocked 1ms or longer we log that as "Blocked".
If we're yielding due to suspending in the middle of the work loop we
log this as "Suspended".
<img width="837" alt="Screenshot 2024-11-16 at 1 15 14 PM"
src="https://github.com/user-attachments/assets/45a858ea-17e6-416c-af1a-78c126e033f3">
If the render doesn't commit because it restarts due to some other
prewarming or because some non-`use()` suspends, it doesn't have from
context components.
<img width="971" alt="Screenshot 2024-11-16 at 1 13 55 PM"
src="https://github.com/user-attachments/assets/a67724f8-702e-4e7d-9499-9ffc09541a61">
The `useActionState` path doesn't work yet because the `use()`
optimization doesn't work there for some reason. But the idea is that it
should mark the time that the component is blocked as Action instead of
Suspended.
When we suspend the render with delay, we won't do any more work until
we get some kind of another update/ping. It's because conceptually
something is suspended and then will update later. We need to highlight
this period to show why it's not doing any work. We fill the empty space
with "Suspended". This stops whenever the same lane group starts
rendering again. Clamped by the preceeding start time/event time/update
time.
<img width="902" alt="Screenshot 2024-11-15 at 1 01 29 PM"
src="https://github.com/user-attachments/assets/acf9dc9a-8fc3-4367-a8b0-d19f9c9eac73">
Ideally we would instead start the next render and suspend the work loop
at all places we suspend. In that mode this will instead show up as a
very long "Render" with a "Suspended" period instead highlighted in the
Components track as one component is suspended. We'll soon have that for
`use()` but not all updates so this covers the rest.
One issue with `useActionState` is that it is implemented as suspending
at the point of the `useActionState` which means that the period of the
Action shows up as a suspended render instead of as an Action which
happens for raw actions. This is not really how you conceptually think
about it so we need some special case for `useActionState`. In the
screenshot above, the first "Suspended" is actually awaiting an Action
and the second "Suspended" is awaiting the data from it.
This PR introduces a new experimental hook `useResourceEffect`, which is
something that we're doing some very early initial tests on.
This may likely not pan out and will be removed or modified if so.
Please do not rely on it as it will break.
This lets us track separately if something was suspended on an Action
using useActionState rather than suspended on Data.
This approach feels quite bloated and it seems like we'd eventually
might want to read more information about the Promise that suspended and
the context it suspended in. As a more general reason for suspending.
The way useActionState works in combination with the prewarming is quite
unfortunate because 1) it renders blocking to update the isPending flag
whether you use it or not 2) it prewarms and suspends the useActionState
3) then it does another third render to get back into the useActionState
position again.
This includes:
- `Interrupted Render`: Interrupted Renders (setState or ping at higher
priority)
- `Prewarm`: Suspended Renders outside a Suspense boundary
(RootSuspendedWithDelay/RootSuspendedAtTheShell)
- `Errored Render`: Render that errored somewhere in the tree (Fatal or
Not) (which may or may not be retried and then complete)
- `Teared Render`: Due to useSyncExternalStore not matching (which will
do another sync attempt)
Suspended Commit:
<img width="893" alt="Screenshot 2024-11-14 at 11 47 40 PM"
src="https://github.com/user-attachments/assets/b25a6a8b-a5e9-4d66-b325-57aef4bf9dad">
Errored with a second recovery attempt that also errors:
<img width="976" alt="Screenshot 2024-11-15 at 12 09 06 AM"
src="https://github.com/user-attachments/assets/9ce52cbb-b587-4f1e-8b67-e51d9073ae5b">
## 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.
It's useful to quickly see where new events are kicking off new
rendering. This uses the new "warning" color (yellow) to do that. This
is to help distinguish it from the purple (secondary color) which is
used for the commit phase which is more of a follow up and it's often
that you have several rerenders within one event which makes it hard to
tell a part where it starts and event otherwise.
For the span marking between previous render within the same event and
the next setState, I use secondary-light (light purple) since it's kind
of still part of the same sequence at that point. It's usually a spawned
render (e.g. setState in useEffect or microtask) but it can also be
sequential flushSync.
I was bothered by that the event name is the only thing that's lower
case so I prefixed it with `Event: ` like the JS traces are.
<img width="1499" alt="Screenshot 2024-11-13 at 7 15 45 PM"
src="https://github.com/user-attachments/assets/0c81c810-6b5d-4fc7-9bc0-d15b53844ade">
It might be a little confusing why our track starts earlier than the JS
one below in the "Main Thread" flamegraph which looks the same. That's
because ours is the start of the event time which is when the click
happens where as the Main Thread one is when the JS event loop gets
around to processing the event.
When you schedule a microtask from render or effect and then call
setState (or ping) from there, the "event" is the event that React
scheduled (which will be a postMessage). The event time of this new
render will be before the last render finished.
We usually clamp these but in this scenario the update doesn't happen
while a render is happening. Causing overlapping events.
Before:
<img width="1229" alt="Screenshot 2024-11-12 at 11 01 30 PM"
src="https://github.com/user-attachments/assets/9652cf3b-b358-453c-b295-1239cbb15952">
Therefore when we finalize a render we need to store the end of the last
render so when we a new update comes in later with an event time earlier
than that, we know to clamp it.
There's also a special case here where when we enter the
`RootDidNotComplete` or `RootSuspendedWithDelay` case we neither leave
the root as in progress nor commit it. Those needs to finalize too.
Really this should be modeled as a suspended track that we haven't added
yet. That's the gap between "Blocked" and "message" below.
After:
<img width="1471" alt="Screenshot 2024-11-13 at 12 31 34 AM"
src="https://github.com/user-attachments/assets/b24f994e-9055-4b10-ad29-ad9b36302ffc">
I also fixed an issue where we may log the same event name multiple
times if we're rendering more than once in the same event. In this case
I just leave a blank trace between the last commit and the next update.
I also adding ignoring of the "message" event at all in these cases when
the event is from React's scheduling itself.
Fixes a bug.
We're supposed to not log "Waiting for Paint" if the passive effect
phase was forced since we weren't really waiting until the paint.
Instead we just log an empty string when we force it to still ensure
continuity.
We should always log the passive phase. This check was in the wrong
place.
In order to make use of the compiler in stable releases (eg React 19 RC,
canary), we need to export the compiler runtime in the stable channel as
well.
Previously we were showing Components inside each lane track but that
meant that as soon as you expanded a lane you couldn't see the other
line so you couldn't get an overview over how well things were
scheduled.
This instead moves all the Components into a single top-level track and
renames the previous one to a "Scheduler" track group.
<img width="1352" alt="Screenshot 2024-11-12 at 8 26 05 PM"
src="https://github.com/user-attachments/assets/590bc6d3-3540-4ee4-b474-5d733b8d8d8d">
That way you can get an overview over what React is working on first and
then right below see which Component is being worked on.
Ideally the "Scheduler" track would be always expanded since each Track
is always just a single row. Now you have to expand each lane to see the
labels but then you're wasting a lot of vertical real estate. There's
currently no option to create this with the Chrome performance.measure
extensions.
<img width="1277" alt="Screenshot 2024-11-12 at 8 26 16 PM"
src="https://github.com/user-attachments/assets/4fc39e35-10ec-4452-ad32-c1c2e6b5e1a8">
In preparation for the next RC, I set this feature flag to true
everywhere. I did not delete the feature flag yet, in case there are yet
more bugs to be discovered.
I also didn't remove the dynamic feature flag from the Meta builds; I'll
let the Meta folks handle that.
This provides less context but skips a lot of noise.
Previously we were including parent components to provide context about
what is rendering but this turns out to be:
1) Very expensive due to the overhead of `performance.measure()` while
profiling.
2) Unactionable noise in the profile that hurt more than it added in
real apps with large trees.
This approach instead just add performance.measure calls for each
component that was marked as PerformedWork (which was used for this
purpose by React Profiler) or had any Effects.
Not everything gets marked with PerformedWork though. E.g. DOM nodes do
not but they can have significant render times since creating them takes
time. We might consider including them if a self-time threshold is met.
Because there is little to no context about the component anymore it
becomes really essential to get a feature from Chrome DevTools that can
link to something with more context like React DevTools.
This reverts commit d3bf32a95806b6d583ef041b8d83781cd686cfd8 which was
part of #30983
When you have very deep trees this trick can cause the top levels to
skew way too much from the real numbers. Creating unbalanced trees.
The bug should have been fixed in Chrome Canary now so that entries
added later are sorted to go first which should've addressed this issue.
## Summary
We have been getting unhandled `TypeError: Cannot convert object to
primitive value` errors in development that only occur when using
devtools. I tracked it down to `console.error()` calls coming from
Apollo Client where one of the arguments is an object without a
prototype (created with `Object.create(null)`). This causes
`formatConsoleArgumentsToSingleString()` in React's devtools to error as
the function does not defend against `String()` throwing an error.
My attempted fix is to introduce a `safeToString` function (naming
suggestions appreciated) which expects `String()` to throw on certain
object and in that case falls back to returning `[object Object]`, which
is what `String({})` would return.
## How did you test this change?
Added a new unit test.
This readme documents React Server Components from `react-server`
package enough to get an implementer started. It's not comprehensive but
it's a beginning point and crucially adds documentation for the
`prerender` API for Flight.
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>
We don't actually want the source mapped version of `.stack` from errors
because that would cause us to not be able to associate it with a source
map in the UIs that need it. The strategy in browsers is more correct
where the display is responsible for source maps.
That's why we disable any custom `prepareStackTrace` like the ones added
by `source-map`. We reset it to `undefined`.
However, when running node with `--enable-source-maps` the default for
`prepareStackTrace` which is a V8 feature (but may exist elsewhere too
like Bun) is a source mapped version of the stack. In those environments
we need to reset it to a default implementation that doesn't apply
source maps.
We already did this in Flight using the `ReactFlightStackConfigV8.js`
config. However, we need this more generally in the
`shared/ReactComponentStackFrame` implementation.
We could always set it to the default implementation instead of
`undefined` but that's unnecessary code in browser builds and it might
lead to slightly different results. For safety and code size, this PR
does it with a fork instead.
All builds specific to `node` or `edge` (or `markup` which is a server
feature) gets the default implementation where as everything else (e.g.
browsers) get `undefined` since it's expected that this is not source
mapped. We don't have to do anything about the equivalent in React
DevTools since React DevTools doesn't run on the server.
## Summary
While fixing ref lifecycles in hidden subtrees in
https://github.com/facebook/react/pull/31379, @rickhanlonii noticed that
we could also add more unit tests for other types of tags to prevent
future regressions during code refactors.
This PR adds more unit tests in the same vein as those added in
https://github.com/facebook/react/pull/31379.
## How did you test this change?
Verified unit tests pass:
```
$ yarn
$ yarn test ReactFreshIntegration-test.js
```
Reverts facebook/react#31403 to reenable lazy context propagation
The disabling was to produce a build that could help track down whether
this flag is causing a possibly related bug in transitions but we don't
intend to disable it just fix forward once we figure out what the
problem is