* Regression test: Effects dropped across roots
See #17066
* [Bugfix] Passive effects loop
The bug
-------
In a multi-root app, certain passive effects (`useEffect`) are never
fired. See #17066.
The underlying problem
----------------------
The implicit contract of `flushPassiveEffects` is that, right after
calling it, there should be no pending passive effects. In the normal
case, in concurrent mode, this is true. But the current implementation
fails to account for the case where a passive effect schedules
synchronous work, which in turn schedules additional passive effects.
This led to `rootWithPendingPassiveEffects` being overwritten in the
commit phase, because an assignment that assumed it was replacing null
was actually replacing a reference to another root, which has the
consequence of dropping passive effects on that root.
The fix
-------
The fix I've chosen here is, at the beginning of the commit phase, keep
flushing passive effects in a loop until there are no more.
This doesn't not change the "public" implementation of
`flushPassiveEffects`, though it arguably should work this way, too. I
say "public" because it's only used by implementation layers on top of
React which we control: mainly, the legacy version of `act` that does
not use the mock Scheduler build. So there's probably still a bug
in that `act` implementation.
I will address `act` in a follow-up. The ideal solution is to replace
the legacy `act` with one implemented directly in the renderer, using a
special testing-only build of React DOM. Since that requires a breaking
change, we'll need an interim solution. We could make the "public" `act`
recursively flush effects in a loop, as I've done for the commit phase.
However, I think a better solution is to stop automatically flushing the
synchronous update queue at the end of `flushPassiveEffects`, and
instead require the caller to explicitly call `flushSyncUpdateQueue` (or
the equivalent) if needed. This follows the same pattern we use
internally in the work loop, which is designed to avoid factoring
hazards like the one that resulted in this bug.
* Unify fields used for createRoot warning and event system
* Warn when doing createRoot twice on the same node
* Stricter check for modern roots
* Unmark asynchronously
* Fix Flow
* [Flight] Basic Integration Test
* Just act()
* Lint
* Remove unnecessary acts
* Use Concurrent Mode
* it.experimental
* Fix prod test by advancing time
* Don't observe initial state
* Return whether to keep flowing in Host config
* Emit basic chunk based streaming in the Flight server
When something suspends a new chunk is created.
* Add reentrancy check
The WHATWG API is designed to be pulled recursively.
We should refactor to favor that approach.
* Basic streaming Suspense support on the client
* Add basic suspense in example
* Add comment describing the protocol that the server generates
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
* [react-is] return correct typeOf value of forwardRef
* [react-devtools-shared] use correct displayName of memo(forwardRef(Component))
* [react-devtools-shared] add resolveFiberType and resolve fiber type of memo recursively
Resolving the fiber type of memo recursively before passing it to getDisplayName
will prevent it from displaying "Anonymous" as displayName for components
wrapped with both memo and forwardRef: memo(forwardRef(Component))
* rework resolveFiberType
There are two similar flags, `debugRenderPhaseSideEffects` and
`debugRenderPhaseSideEffectsForStrictMode`. The strict mode one is the
only one that is actually used. I think originally the theory is that
we would one day turn it on for all components, even outside strict
mode. But what we'll do instead is migrate everyone to strict mode.
The only place `debugRenderPhaseSideEffects` was being used was in
an internal test file. I rewrote those tests to use public APIs.
* Rename ReactFlightStreamer -> ReactFlightServer
* Unify Browser/Node stream tests into one file and use the client reader
* Defer to the actual ReactDOM for HTML rendering for now
This will need to use a variant of Fizz to do inline SSR in Flight.
However, I don't want to build the whole impl right now but also don't
want to exclude the use case yet. So I outsource it to the existing
renderer. Ofc, this doesn't work with Suspense atm.
* Change demo to server
* Expose client in package.json
* Reorganize tests
We don't want unit tests but instead test how both server and clients work
together. So this merges server/client test files.
* Fill in the client implementation a bit
* Use new client in fixture
* Add Promise/Uint8Array to lint rule
I'll probably end up deleting these deps later but they're here for now.
`it.experimental` marks that a test only works in Experimental builds.
It also asserts that a test does *not* work in the stable builds. The
main benefit is that we're less likely to accidentally expose an
experimental API before we intend. It also forces us to un- mark an
experimental test once it become stable.
* Add CodeSandbox CI Config
* Add default sandbox to build
* Make build more efficient and add scheduler
* Force build
* Add scheduler image
* Add scheduler/tracing to the build
* Force another build
* Add Flight Build and Unify HostFormat Config between Flight and Fizz
* Add basic resolution of models
* Add basic Flight fixture
Demonstrates the streaming protocol.
* Rename to flight-server to distinguish from the client parts
* Add Flight Client package and entry point
* Fix fixture
Already mounted rows that resuspend may be considered as part of a tail
if they're at the end. However, for purposes of the tail="..." option
they don't get deleted. We deal with that in cutOffTailIfNeeded.
However, if they're also the first to suspend in the "hidden" case, we have
a special case that deletes the actual rendered row. This needs to consider
if that row was already mounted or things go wrong.