Stacked on #30960 and #30966. Behind the enableComponentPerformanceTrack
flag.
This is the first step of performance logging. This logs the start and
end time of a component render in the passive effect phase. We use the
data we're already tracking on components when the Profiler component or
DevTools is active in the Profiling or Dev builds. By backdating this
after committing we avoid adding more overhead in the hot path. By only
logging things that actually committed, we avoid the costly unwinding of
an interrupted render which was hard to maintain in earlier versions.
We already have the start time but we don't have the end time. That's
because `actualStartTime + actualDuration` isn't enough since
`actualDuration` counts the actual CPU time excluding yields and
suspending in the render.
Instead, we infer the end time to be the start time of the next sibling
or the complete time of the whole root if there are no more siblings. We
need to pass this down the passive effect tree. This will mean that any
overhead and yields are attributed to this component's span. In a follow
up, we'll need to start logging these yields to make it clear that this
is not part of the component's self-time.
In follow ups, I'll do the same for commit phases. We'll also need to
log more information about the phases in the top track. We'll also need
to filter out more components from the trees that we don't need to
highlight like the internal Offscreen components. It also needs polish
on colors etc.
Currently, I place the components into separate tracks depending on
which lane currently committed. That way you can see what was blocking
Transitions or Suspense etc. One problem that I've hit with the new
performance.measure extensions is that these tracks show up in the order
they're used which is not the order of priority that we use. Even when
you add fake markers they have to actually be within the performance run
since otherwise the calls are noops so it's not enough to do that once.
However, I think this visualization is actually not good because these
trees end up so large that you can't see any other lanes once you expand
one. Therefore, I think in a follow up I'll actually instead switch to a
model where Components is a single track regardless of lane since we
don't currently have overlap anyway. Then the description about what is
actually rendering can be separate lanes.
<img width="1512" alt="Screenshot 2024-09-15 at 10 55 55 PM"
src="https://github.com/user-attachments/assets/5ca3fa74-97ce-40c7-97f7-80c1dd7d6470">
<img width="1512" alt="Screenshot 2024-09-15 at 10 56 27 PM"
src="https://github.com/user-attachments/assets/557ad65b-4190-465f-843c-0bc6cbb9326d">
DiffTrain build for [f2df5694f2](https://github.com/facebook/react/commit/f2df5694f2be141954f22618fd3ad035203241a3)
This flag will be used to gate a new timeline profiler that's integrate
with the Performance Tab and the new performance.measure extensions in
Chrome.
It replaces the existing DevTools feature so this disables
enableSchedulingProfiler when it is enabled since they can interplay in
weird ways potentially.
This means that experimental React now disable scheduling profiler and
enables this new approach.
DiffTrain build for [0eab377a96](https://github.com/facebook/react/commit/0eab377a96099f0121009c8968c49d13d4e00bd1)
In a past update we made render and prerender have different work
scheduling behavior because these methods are meant to be used in
differeent environments with different performance tradeoffs in mind.
For instance to prioritize streaming we want to allow as much IO to
complete before triggering a round of work because we want to flush as
few intermediate UI states. With Prerendering there will never be any
intermediate UI states so we can more aggressively render tasks as they
complete.
One thing we've found is that even during render we should ideally kick
off work immediately. This update normalizes the intitial work for
render and prerender to start in a microtask. Choosing microtask over
sync is somewhat arbitrary but there really isn't a reason to make them
different between render/prerender so for now we'll unify them and keep
it as a microtask for now.
This change also updates pinging behavior. If the request is still in
the initial task that spawned it then pings will schedule on the
microtask queue. This allows immediately available async APIs to resolve
right away. The concern with doing this for normal pings is that it
might crowd out IO events but since this is the initial task there would
be IO to already be scheduled.
DiffTrain build for [fc5ef50da8](https://github.com/facebook/react/commit/fc5ef50da8e975a569622d477f1fed54cb8b193d)
In #26624, the ability to mark a client reference module as `async` in
the React client manifest was removed because it was not utilized by
Webpack, neither in `ReactFlightWebpackPlugin` nor in Next.js. However,
some bundlers and frameworks are sophisticated enough to properly handle
and identify async ESM modules (e.g., client component modules with
top-level `await`), most notably Turbopack in Next.js. Therefore, we
need to consider the `async` flag in the client manifest when resolving
the client reference metadata on the server. The SSR manifest cannot
override this flag, meaning that if a module is async, it must remain
async in all client environments.
x-ref: https://github.com/vercel/next.js/pull/70022
DiffTrain build for [5deb78223a](https://github.com/facebook/react/commit/5deb78223a269a6cb1706da8ec6aad8c007cab03)
At some point this trick was added to initialize the value first to NaN
and then replace them with zeros and negative ones.
This is to address the issue noted in
https://github.com/facebook/react/issues/14365 where these hidden
classes can be initialized to SMIs at first and then deopt when we
realize they're actually doubles.
However, this fix has been long broken and has deopted the profiling
build for years because closure compiler optimizes out the first write.
I'm not sure because I haven't A/B-tested this in the JIT yet but I
think we can use negative zero and -1.1 as the initial values instead
since they're not simple integers. Negative zero `===` zero (but not
Object.is) so is the same as far as our code is concerned. The negative
value is just `< 0` comparisons.
DiffTrain build for [94e4acaa14](https://github.com/facebook/react/commit/94e4acaa1477e65cac02ba86058cde0afe4c8f1f)
This is only in the same experimental exports as `resume`. Useful with
Postpone/Halt.
We already have `prerender()` to create a partial tree with postponed
state. We also have `resume()` to dynamically resume such a tree.
This lets you do a new prerender by resuming an already existing
postponed state. Basically creating a chain of preludes. The next
prelude would include the scripts to patch up the document.
This mostly just works since both prerender and resume are already
implemented using the same code so we just enable both at the root. I'm
sure we'll find some edge cases since this wasn't considered when it was
first written but so far I've only found an unrelated existing bug with
`keyPath` fixed here.
DiffTrain build for [473522093d](https://github.com/facebook/react/commit/473522093d3dd95582729d01cd5c0d15dcc9cd3b)
When a component suspends and is replaced by a fallback, we should start
prerendering the fallback immediately, even before any new data is
received. During the retry, we can enter prerender mode directly if
we're sure that no new data was received since we last attempted to
render the boundary.
To do this, when completing the fallback, we leave behind a pending
retry lane on the Suspense boundary. Previously we only did this once a
promise resolved, but by assigning a lane during the complete phase, we
will know that there's speculative work to be done.
Then, upon committing the fallback, we mark the retry lane as suspended
— but only if nothing was pinged or updated in the meantime. That allows
us to immediately enter prerender mode (i.e. render without skipping any
siblings) when performing the retry.
DiffTrain build for [d6cb4e7713](https://github.com/facebook/react/commit/d6cb4e771341ff82489c00f4907990cb8a75696b)
If something suspends in the shell — i.e. we won't replace the suspended
content with a fallback — we might as well prerender the siblings during
the current render pass, instead of spawning a separate prerender pass.
This is implemented by setting the "is prerendering" flag to true
whenever we suspend in the shell. But only if we haven't already skipped
over some siblings, because if so, then we need to schedule a separate
prerender pass regardless.
DiffTrain build for [66cf2cfc8a](https://github.com/facebook/react/commit/66cf2cfc8a8c4b09d2b783fd7302ae6b24150935)
This reverts #19603.
Before:
<img width="724" alt="Screenshot 2024-08-28 at 12 07 29 AM"
src="https://github.com/user-attachments/assets/0613088f-c013-4f1c-92c3-fbdae8c1f109">
After:
<img width="771" alt="Screenshot 2024-08-28 at 12 08 13 AM"
src="https://github.com/user-attachments/assets/eef21bee-d11f-4f0a-9147-053a163f720f">
Consensus seems to be that while the purple on is a bit clearer and
easier to read. The purple is not on brand so it doesn't look like
React. It looks ugly. It's distracting (too eye catching). Taking away
attention from other tabs in an unfair way.
It also gets worse with more tabs added. We plan on both adding another
tab and panes inside other tabs (elements/sources) soon. Each needs to
be marked somehow as part of React but spelling it out is too long.
Putting inside a second tab means two clicks and takes away real-estate
from our extension and doesn't solve the problem with extension panes in
other tabs. We also plan on adding multiple different tracks to the
Performance tab which also needs a name other than just React and
spelling out React as a prefix is too long. The Emoji is too
distracting. So it seems best to uniformly apply the symbol - albeit it
might just look like a dot to many.
Dark mode looks close to on brand:
<img width="1089" alt="Screenshot 2024-08-28 at 12 32 50 AM"
src="https://github.com/user-attachments/assets/7175a540-4241-4c26-9e4d-4d367873af57">
DiffTrain build for [d160aa0fbb](https://github.com/facebook/react/commit/d160aa0fbb1bd2d00ea8c771c551c9cb5b47f1e9)
Fixes the bug that @alexmckenley and @mofeiZ found where setState-in-render can reset useMemoCache and cause an infinite loop. The bug was that renderWithHooksAgain() was not resetting hook state when rerendering (so useMemo values were preserved) but was resetting the updateQueue. This meant that the entire memo cache was cleared on a setState-in-render.
The fix here is to call a new helper function to clear the update queue. It nulls out other properties, but for memoCache it just sets the index back to zero.
ghstack-source-id: fc0947ce21
Pull Request resolved: https://github.com/facebook/react/pull/30889
DiffTrain build for [727b361528](https://github.com/facebook/react/commit/727b3615287074ddaa28069bfbd2dfee8cf73575)
Stacked on #30881.
Move `runWithFiberInDEV` from the recursive part of the commit phase and
instead wrap each call into user space. These should really map 1:1 with
where we're using `try/catch` since that's where we're calling into user
space.
The goal of this is to avoid the extra stack frames added by
`enableOwnerStacks` in the recursive parts to avoid stack overflow. This
way we only have a couple of extra at the end of the stack instead of a
couple of extra at every depth of the tree.
DiffTrain build for [a03254bc60](https://github.com/facebook/react/commit/a03254bc60b06c535c37e43c53b1fd40757b2ef4)
This is mostly just moves and same code extracted into utility
functions.
This is to help clarify what needs to be wrapped in try/catch and
runWithFiberInDEV. I'll do the runWithFiberInDEV changes in a follow up.
This leaves ReactCommitWork mostly to do matching on the tag and the
recursive loops.
DiffTrain build for [fe03c56d1e](https://github.com/facebook/react/commit/fe03c56d1e51379a18676b04cf185e76f04cd457)
Adds the concept of a "prerender". These special renders are spawned
whenever something suspends (and we're not already prerendering).
The purpose is to move speculative rendering work into a separate phase
that does not block the UI from updating. For example, during a
transition, if something suspends, we should not speculatively prerender
siblings that will be replaced by a fallback in the UI until *after* the
fallback has been shown to the user.
DiffTrain build for [e10e868182](https://github.com/facebook/react/commit/e10e8681824e56c10fdb14e0359d878bcd748937)
### Based on
- #30761
- #30759
---
`use` has an optimization where in some cases it can suspend the work
loop during the render phase until the data has resolved, rather than
unwind the stack and lose context. However, the current implementation
is not compatible with sibling prerendering. So I've temporarily
disabled it until the sibling prerendering has been refactored. We will
add it back in a later step.
DiffTrain build for [8b4c54c00f](https://github.com/facebook/react/commit/8b4c54c00f5c047a72a4cecc2689196786c3e5ff)
If we see the "Maximum call stack size exceeded" error we know we've hit
stack overflow. We can recover from this by spawning a new task and
trying again. Effectively a zero-cost trampoline in the normal case. The
new task will have a clean stack. If you have a lot of siblings at the
same depth that hits the limit you can end up hitting this once for each
sibling but within that new sibling you're unlikely to hit this again.
So it's not too expensive.
If it errors again in the retryTask pass, the other error handling takes
over which causes this to be able to still not infinitely stall. E.g.
when the component itself throws an error like this.
It's still better to increase the stack limit for performance if you
have a really deep tree but it doesn't really hurt to be able to recover
since it's zero cost when it doesn't happen.
We could do the same thing for Flight. Those trees don't tend to be as
deep but could happen.
DiffTrain build for [96aca5f4f3](https://github.com/facebook/react/commit/96aca5f4f3d7fbe0c13350f90031d8ec4c060ccb)
This is a refactor of the fix in #27505.
When a transition update is scheduled by a popstate event, (i.e. a back/
forward navigation) we attempt to render it synchronously even though
it's a transition, since it's likely the previous page's data is cached.
In #27505, I changed the implementation so that it only "upgrades" the
priority of the transition for a single attempt. If the attempt
suspends, say because the data is not cached after all, from then on it
should be treated as a normal transition.
But it turns out #27505 did not work as intended, because it relied on
marking the root with pending synchronous work (root.pendingLanes),
which was never cleared until the popstate update completed.
The test scenarios I wrote accidentally worked for a different reason
related to suspending the work loop, which I'm currently in the middle
of refactoring.
DiffTrain build for [ee7f6757c4](https://github.com/facebook/react/commit/ee7f6757c446c4e79ecc7e2bc22b8c9b712834b7)
When we introduced prerendering for flight we modeled an abort of a
flight prerender as having unfinished rows. This is similar to how
postpone was already implemented when you postponed from "within" a
prerender using React.unstable_postpone. However when aborting with a
postponed instance every boundary would be eagerly marked for client
rendering which is more akin to prerendering and then resuming with an
aborted signal.
The insight with the flight work was that it's not so much the postpone
that describes the intended semantics but the abort combined with a
prerender. So like in flight when you abort a prerender and enableHalt
is enabled boundaries and the shell won't error for any reason. Fizz
will still call onPostpone and onError according to the abort reason but
the consuemr of the prerender should expect to resume it before trying
to use it.
DiffTrain build for [85180b8cf8](https://github.com/facebook/react/commit/85180b8cf84274795986c8f2c8473f8816db8b7b)
When aborting with a postpone value boundaries are put into client
rendered mode even during prerenders. This doesn't follow the postpoen
semantics of the rest of fizz where during a prerender a postpone is
tracked and it will leave holes in tracked postpone state that can be
resumed. This change updates this behavior to match the postpones
semantics between aborts and imperative postpones.
DiffTrain build for [2505bf9b34](https://github.com/facebook/react/commit/2505bf9b3400c6a00381e86d30b495935f5339df)
It is possible to throw after aborting during a render and we were not
properly tracking this. We use an AbortSigil to mark whether a rendering
task needs to abort but the throw interrupts that and we end up handling
an error on the error pathway instead.
This change reworks the abort-while-rendering support to be robust to
throws after calling abort
DiffTrain build for [7954db9398](https://github.com/facebook/react/commit/7954db9398b9afa962167577a6c6940be3856c39)
When aborting with a postpone value in Fizz if any tasks are still
pending in the root while prerendering the prerender will fatally error.
This is different from postponing imperatively in a root task and really
the semantics should be the same. This change updates React to treat an
abort with a postpone value as a postponed root rather than a fatal
error.
DiffTrain build for [a7d1240c96](https://github.com/facebook/react/commit/a7d1240c962d2fdeac3ba31f1fdc12b5be4bbd2e)
We use static dependency injection. We shouldn't use this dynamic
dependency injection we do for DevTools internals. There's also meta
programming like spreading and stuff that isn't needed.
This moves the config from `injectIntoDevTools` to the FiberConfig so it
can be statically resolved.
Closure Compiler has some trouble generating optimal code for this
anyway so ideally we'd refactor this further but at least this is better
and saves a few bytes and avoids some code paths (when minified).
DiffTrain build for [146df7c311](https://github.com/facebook/react/commit/146df7c311831a1d5b35f3783dae4a7030638fcf)
This is not used by DevTools since it has its own implementation of it.
This function is getting removed since `findDOMNode` is getting removed
so we shouldn't keep around extra bytes unnecessarily.
There is also `findHostInstancesForRefresh` which should really be
implemented on the `react-refresh` side. Not using an injection but
that's a heavier lift and only affects `__DEV__`.
DiffTrain build for [bea5a2bc46](https://github.com/facebook/react/commit/bea5a2bc46cc95713392175c3c6307e49c14cd98)
Currently if you abort a Fizz render during rendering the render will
not complete correctly because there are inconsistencies with task
counting. This change updates the abort implementation to allow you to
abort from within a render itself. We already landed a similar change
for Flight in #29764
DiffTrain build for [a451de014c](https://github.com/facebook/react/commit/a451de014ca71718aee924bb57d5b4a1d87e20f2)
When a Fizz render is closing but not yet closed it's possible that
pinged tasks can spawn more work. The point of the closing state is to
allow time to start piping/reading the underlying stream but
semantically the render is finished at that point so work should no
longer happen.
DiffTrain build for [9938e248fe](https://github.com/facebook/react/commit/9938e248feecc6410738cc905744836d6f78c7f8)
When prerendering it can be convenient to abort the prerender while
rendering. However if any Suspense fallbacks have not yet rendered
before the abort happens the fallback itself will error and cause the
nearest parent Suspense boundary to render a fallback instead.
Prerenders are by definition not time critical so the prioritization of
children over fallbacks which makes sense for render isn't similarly
motivated for prerender. Given this, this change updates fallback
rendering during a prerender to attempt the fallback before attempting
children.
DiffTrain build for [d17e9d1ce5](https://github.com/facebook/react/commit/d17e9d1ce566276fc54a8ea27f4e9ea1fa434e62)
Stacked on #30427.
Most hooks and such are called inside renders which already have these
on the stack but life-cycles that call out on them are useful to cut off
too.
Typically we don't create JSX in here so they wouldn't be part of owner
stacks anyway but they can be apart of plain stacks such as the ones
prefixes to console logs or printed by error dialogs.
This lets us cut off any React internals below. This should really be
possible using just ignore listing too ideally.
At this point we should maybe just build a Babel plugin that lets us
annotate a function to need to have this name.
DiffTrain build for [da4abf0047](https://github.com/facebook/react/commit/da4abf0047cf6dc6d9bff505bd93815264c8c3b7)