In a previous version of act(), we used a dummy dom element to flush effects. This doesn't need to exist anymore, and this PR removes it. The warning doesn't need to be there either (React will fire a wrong renderer act warning if needed).
We have behaviour divergence for act() between prod and dev (specifically, act() + concurrent mode does not flush fallbacks in prod. This doesn't affect anyone in OSS yet)
We also don't have a good story for writing tests in prod (and what from what I gather, nobody really writes tests in prod mode).
We could have wiped out act() in prod builds, except that _we_ ourselves use act() for our tests when we run them in prod mode.
This PR is a compromise to all of this. We will log a warning if you try to use act() in prod mode, and we silence it in our test suites.
Concurrent/Batched mode tests should always be run with a mocked scheduler (v17 or not). This PR adds a warning for the same. I'll put up a separate PR to the docs with a page detailing how to mock the scheduler.
Not returning the value of flushPassiveEffects() in flushWork() meant that with async act, we wouldn't flush all work with cascading effects. This PR fixes that oversight, and adds some tests to catch this in the future.
Given this snippet:
```jsx
TestRenderer.act(() => {
TestUtils.act(() => {
TestRenderer.create(<Effecty />);
});
});
```
We want to make sure that all work is only flushed on exiting the outermost act().
Now, naively doing this based on actingScopeDepth would work with a mocked scheduler, where flushAll() would flush all work across renderers.
This doesn't work without mocking the scheduler though; and where flushing work only works per renderer. So we disable this behaviour for a non-mocked scenario. This seems like an ok tradeoff.
* [fail] reset IsThisRendererActing correctly
I missed this in https://github.com/facebook/react/pull/16039. I'd pointed at the wrong previous state, corrupting it in further use. This PR fixes that, and adds a test to make sure it doesn't happen again.
* warn for unacted effects only in strict mode
* allow nested `act()`s from different renderers
There are usecases where multiple renderers need to oprate inside an act() scope
- ReactDOM.render being used inside another component tree. The parent component will be rendered using ReactTestRenderer.create for a snapshot test or something.
- a ReactDOM instance interacting with a ReactTestRenderer instance (like for the new devtools)
This PR changes the way the acting sigils operate to allow for this. It keeps 2 booleans, one attached to React, one attached to the renderer. act() changes these values, and the workloop reads them to decide what warning to trigger.
I also renamed shouldWarnUnactedUpdates to warnsIfNotActing
* s/ReactIsActing/IsSomeRendererActing and s/ReactRendererIsActing/IsThisRendererActing
* use toWarnDev for dom fixture tests
forks toWarnDev from root into fixture/dom, updates tes tests to use it
* disable act() warnings for react-art()
- For 'secondary' renderers like react-act, we don't want to fire missing act() warnings; the wrapping renderer will fire warnings anyway, and when it flushes, it flushes effects *across* renderers.
- I could have used isPrimaryRenderer as the flag, but this is marked as false for react-test-renderer, and we *do* want the warning to fire for it. Hence a new flag.
* add missing dependency `art` to fixtures/dom
* reset scope depth on synchronous errors
we weren't resetting the acting scope depth on sync errors thrown in the callback. this fixes that.
* typos
* add a test to make sure sync error propagate
* warn when using the wrong renderer's act around another renderer's updates
like it says. it uses a real object as the sigil (instead of just a boolean). specifically, it uses a renderer's flushPassiveEffects as the sigil. We also run tests for this separate from our main suite (which doesn't allow loading multiple renderers in a suite), but makes sure to run this in CI as well.
* unneeded (and wrong) comment
* run the dom fixture on CI
* update the sigil only in __DEV__
* remove the obnoxious comment
* use an explicit export for the sigil
* Rename ReactFiberScheduler to ReactFiberWorkLoop
The scheduling part is mostly extracted out to the scheduler package.
What's remaining is mostly around the loop around each section of work.
I name it something with Work in it because it's very related to the
BeginWork, CompleteWork and UnwindWork sections.
* Extract throwException from UnwindWork
Our throwing works more like algebraic effects in that it's a separate
phase where we find a handler and we later unwind.
* s/flushPassiveEffects/unstable_flushWithoutYielding
a first crack at flushing the scheduler manually from inside act(). uses unstable_flushWithoutYielding(). The tests that changed, mostly replaced toFlushAndYield(...) with toHaveYielded(). For some tests that tested the state of the tree before flushing effects (but still after updates), I replaced act() with bacthedUpdates().
* ugh lint
* pass build, flushPassiveEffects returns nothing now
* pass test-fire
* flush all work (not just effects), add a compatibility mode
of note, unstable_flushWithoutYielding now returns a boolean much like flushPassiveEffects
* umd build for scheduler/unstable_mock, pass the fixture with it
* add a comment to Shcduler.umd.js for why we're exporting unstable_flushWithoutYielding
* run testsutilsact tests in both sync/concurrent modes
* augh lint
* use a feature flag for the missing mock scheduler warning
I also tried writing a test for it, but couldn't get the scheduler to unmock. included the failing test.
* Update ReactTestUtilsAct-test.js
- pass the mock scheduler warning test,
- rewrite some tests to use Scheduler.yieldValue
- structure concurrent/legacy suites neatly
* pass failing tests in batchedmode-test
* fix pretty/lint/import errors
* pass test-build
* nit: pull .create(null) out of the act() call
* Add Batched Mode
React has an unfortunate quirk where updates are sometimes synchronous
-- where React starts rendering immediately within the call stack of
`setState` — and sometimes batched, where updates are flushed at the
end of the current event. Any update that originates within the call
stack of the React event system is batched. This encompasses most
updates, since most updates originate from an event handler like
`onClick` or `onChange`. It also includes updates triggered by lifecycle
methods or effects. But there are also updates that originate outside
React's event system, like timer events, network events, and microtasks
(promise resolution handlers). These are not batched, which results in
both worse performance (multiple render passes instead of single one)
and confusing semantics.
Ideally all updates would be batched by default. Unfortunately, it's
easy for components to accidentally rely on this behavior, so changing
it could break existing apps in subtle ways.
One way to move to a batched-by-default model is to opt into Concurrent
Mode (still experimental). But Concurrent Mode introduces additional
semantic changes that apps may not be ready to adopt.
This commit introduces an additional mode called Batched Mode. Batched
Mode enables a batched-by-default model that defers all updates to the
next React event. Once it begins rendering, React will not yield to
the browser until the entire render is finished.
Batched Mode is superset of Strict Mode. It fires all the same warnings.
It also drops the forked Suspense behavior used by Legacy Mode, in favor
of the proper semantics used by Concurrent Mode.
I have not added any public APIs that expose the new mode yet. I'll do
that in subsequent commits.
* Suspense in Batched Mode
Should have same semantics as Concurrent Mode.
* Use RootTag field to configure type of root
There are three types of roots: Legacy, Batched, and Concurrent.
* flushSync should not flush batched work
Treat Sync and Batched expiration times separately. Only Sync updates
are pushed to our internal queue of synchronous callbacks.
Renamed `flushImmediateQueue` to `flushSyncCallbackQueue` for clarity.