The following APIs have been added to the `react` stable entry point:
* `SuspenseList`
* `startTransition`
* `unstable_createMutableSource`
* `unstable_useMutableSource`
* `useDeferredValue`
* `useTransition`
The following APIs have been added or removed from the `react-dom` stable entry point:
* `createRoot`
* `unstable_createPortal` (removed)
The following APIs have been added to the `react-is` stable entry point:
* `SuspenseList`
* `isSuspenseList`
The following feature flags have been changed from experimental to true:
* `enableLazyElements`
* `enableSelectiveHydration`
* `enableSuspenseServerRenderer`
* DevTools flushes updated passive warning/error info after delay
Previously this information was not flushed until the next commit, but this provides a worse user experience if the next commit is really delayed. Instead, the backend now flushes only the warning/error counts after a delay. As a safety, if there are already any pending operations in the queue, we bail.
Co-authored-by: eps1lon <silbermann.sebastian@gmail.com>
DevTools was built with a fork of an early idea for how Suspense cache might work. This idea is incompatible with newer APIs like `useTransition` which unfortunately prevented me from making certain UX improvements. This PR swaps out the primary usage of this cache (there are a few) in favor of the newer `unstable_getCacheForType` and `unstable_useCacheRefresh` APIs. We can go back and update the others in follow up PRs.
### Messaging changes
I've refactored the way the frontend loads component props/state/etc to hopefully make it better match the Suspense+cache model. Doing this gave up some of the small optimizations I'd added but hopefully the actual performance impact of that is minor and the overall ergonomic improvements of working with the cache API make this worth it.
The backend no longer remembers inspected paths. Instead, the frontend sends them every time and the backend sends a response with those paths. I've also added a new "force" parameter that the frontend can use to tell the backend to send a response even if the component hasn't rendered since the last time it asked. (This is used to get data for newly inspected paths.)
_Initial inspection..._
```
front | | back
| -- "inspect" (id:1, paths:[], force:true) ---------> |
| <------------------------ "inspected" (full-data) -- |
```
_1 second passes with no updates..._
```
| -- "inspect" (id:1, paths:[], force:false) --------> |
| <------------------------ "inspected" (no-change) -- |
```
_User clicks to expand a path, aka hydrate..._
```
| -- "inspect" (id:1, paths:['foo'], force:true) ----> |
| <------------------------ "inspected" (full-data) -- |
```
_1 second passes during which there is an update..._
```
| -- "inspect" (id:1, paths:['foo'], force:false) ---> |
| <----------------- "inspectedElement" (full-data) -- |
```
### Clear errors/warnings transition
Previously this meant there would be a delay after clicking the "clear" button. The UX after this change is much improved.
### Hydrating paths transition
I also added a transition to hydration (expanding "dehyrated" paths).
### Better error boundaries
I also added a lower-level error boundary in case the new suspense operation ever failed. It provides a better "retry" mechanism (select a new element) so DevTools doesn't become entirely useful. Here I'm intentionally causing an error every time I select an element.
### Improved snapshot tests
I also migrated several of the existing snapshot tests to use inline snapshots and added a new serializer for dehydrated props. Inline snapshots are easier to verify and maintain and the new serializer means dehydrated props will be formatted in a way that makes sense rather than being empty (in external snapshots) or super verbose (default inline snapshot format).
DevTools has a feature to force a Suspense boundary to show a fallback.
This feature causes us to skip the first render pass (where we render
the primary children) and go straight to rendering the fallback.
There's a Legacy Mode-only codepath that failed to take this scenario
into account, instead assuming that whenever a fallback is being
rendered, it was preceded by an attempt to render the primary children.
SuspenseList can also cause us to skip the first pass, but the relevant
branch is Legacy Mode-only, and SuspenseList is not supported in
Legacy Mode.
Fixes a test that I had temporarily disabled when upstreaming the Lanes
implementation in #19108.
* Add autofix to cross-fork lint rule
* replace-fork: Replaces old fork contents with new
For each file in the new fork, copies the contents into the
corresponding file of the old fork, replacing what was already there.
In contrast to merge-fork, which performs a three-way merge.
* Replace old fork contents with new fork
First I ran `yarn replace-fork`.
Then I ran `yarn lint` with autofix enabled. There's currently no way to
do that from the command line (we should fix that), so I had to edit the
lint script file.
* Manual fix-ups
Removes dead branches, removes prefixes from internal fields. Stuff
like that.
* Fix DevTools tests
DevTools tests only run against the old fork, which is why I didn't
catch these earlier.
There is one test that is still failing. I'm fairly certain it's related
to the layout of the Suspense fiber: we no longer conditionally wrap the
primary children. They are always wrapped in an extra fiber.
Since this has been running in www for weeks without major issues, I'll
defer fixing the remaining test to a follow up.
We've been shipping unprefixed experimental APIs (like `createRoot` and
`useTransition`) to the Experimental release channel, with the rationale
that because these APIs do not appear in any stable release, we're free
to change or remove them later without breaking any downstream projects.
What we didn't consider is that downstream projects might be tempted to
use feature detection:
```js
const useTransition = React.useTransition || fallbackUseTransition;
```
This pattern assumes that the version of `useTransition` that exists in
the Experimental channel today has the same API contract as the final
`useTransition` API that we'll eventually ship to stable.
To discourage feature detection, I've added an `unstable_` prefix to
all of our unstable APIs.
The Facebook builds still have the unprefixed APIs, though. We will
continue to support those; if we make any breaking changes, we'll
migrate the internal callers like we usually do. To make testing easier,
I added the `unstable_`-prefixed APIs to the www builds, too. That way
our tests can always use the prefixed ones without gating on the
release channel.
* Add feature flag
* Split stack from current fiber
You can get stack from any fiber, not just current.
* Refactor description of component frames
These should use fiber tags for switching. This also puts the relevant code
behind DEV flags.
* We no longer expose StrictMode in component stacks
They're not super useful and will go away later anyway.
* Update tests
Context is no longer part of SSR stacks. This was already the case on the
client.
forwardRef no longer is wrapped on the stack. It's still in getComponentName
but it's probably just noise in stacks. Eventually we'll remove the wrapper
so it'll go away anyway. If we use native stack frames they won't have this
extra wrapper.
It also doesn't pick up displayName from the outer wrapper. We could maybe
transfer it but this will also be fixed by removing the wrapper.
* Forward displayName onto the inner function for forwardRef and memo in DEV
This allows them to show up in stack traces.
I'm not doing this for lazy because lazy is supposed to be called on the
consuming side so you shouldn't assign it a name on that end. Especially
not one that mutates the inner.
* Use multiple instances of the fake component
We mutate the inner component for its name so we need multiple copies.
1. Add a Store test for memo, lazy, and forwardRef components
2. Remove dead code for React.lazy
3. Update DT tests to include HOC badge names in the serialized store
* SuspenseList support in DevTools
This adds SuspenseList tags to DevTools so that the name properly shows
up.
It also switches to use the tag instead of Symbol type for Suspense
components. We shouldn't rely on the type for any built-ins since that
field will disappear from the fibers. How the Fibers get created is an
implementation detail that can change e.g. with a compiler or if we
use instanceof checks that are faster than symbol comparisons.
* Add SuspenseList test to shell app