* [Scheduler] requestPaint
Signals to Scheduler that the browser needs to paint the screen. React
will call it in the commit phase. Scheduler will yield at the end of
the current frame, even if there is no pending input.
When `isInputPending` is not available, this has no effect, because we
yield at the end of every frame regardless.
React will call `requestPaint` in the commit phase as long as there's at
least one effect. We could choose not to call it if none of the effects
are DOM mutations, but this is so rare that it doesn't seem worthwhile
to bother checking.
* Fall back gracefully if requestPaint is missing
* Add SuspenseList component type
* Push SuspenseContext for SuspenseList
* Force Suspense boundaries into their fallback state
In the "together" mode, we do a second render pass that forces the
fallbacks to stay in place, if not all can unsuspend at once.
* Add test
* Transfer thennables to the SuspenseList
This way, we end up retrying the SuspenseList in case the nested boundary
that just suspended doesn't actually get mounted with this set of
thennables. This happens when the second pass renders the fallback
directly without first attempting to render the content.
* Add warning for unsupported displayOrder
* Add tests for nested sibling boundaries and nested lists
* Fix nested SuspenseList forwarding thennables
* Rename displayOrder to revealOrder
Display order has some "display list" connotations making it sound like
a z-index thing.
Reveal indicates that this isn't really about when something gets rendered
or is ready to be rendered. It's about when content that is already there
gets to be revealed.
* Add test for avoided boundaries
* Make SuspenseList a noop in legacy mode
* Use an explicit suspense list state object
This will be used for more things in the directional case.
* Same as previous commit, but for Flare
I don't know what the public API for setting the event priority should
be. Right now it accepts a numeric enum, but is this what we want?
Maybe it should be a string enum? I've punted on this for now.
* Add test for hover events
* [Events] Add EventPriority enum
React DOM's DispatchConfig for synthetic events has an `isDiscrete`
field that affects how updates triggered by an event are scheduled.
Events are either discrete or continuous.
This commit adds an additional type of configuration where an event
has user-blocking priority, but is not discrete. E.g. updates triggered
by hover are more important than the default, but they don't need to
be processed serially. Because there are now three types of event
priority instead of two, I've replaced the `isDiscrete` boolean with an
enum: `eventPriority`.
This commit implements the new enum value but does not change any
behavior. I'll enable it behind a feature flag in the next commit.
I've only implemented this in the legacy event system. I'll leave Flare
for a follow-up.
* enableUserBlockingEvents feature flag
Adds a feature flag to increase the priority of events like `mouseover`,
without making them discrete.
This commit is a follow-up to https://github.com/facebook/react/pull/15604, which explains more of the rationale behind moving React Native to path-based imports and the work needed in the React repository. In that linked PR, the generated renderers were updated but not the shims; this commit updates the shims.
The problem is that FB needs a different copy of the built renderers than the OSS versions so we need a way for FB code to import different modules than in OSS. This was previously done with Haste, but with the removal of Haste from RN, we need another mechanism. Talking with cpojer, we are using a `.fb.js` extension that Metro can be configured to prioritize over `.js`.
This commit generates FB's renderers with the `.fb.js` extension and OSS renderers with just `.js`. This way, FB can internally configure Metro to use the `.fb.js` implementations and OSS will use the `.js` ones, letting us swap out which implementation gets bundled.
Test Plan: Generated the renderers and shims with `yarn build` and then verified that the generated shims don't contain any Haste-style imports. Copied the renderers and shims into RN manually and launched the RNTester app to verify it loads end-to-end. Added `.fb.js` to the extensions in `metro.config.js` and verified that the FB-specific bundles loaded.
* Add suspendIfNeeded API and a global scope to track it
Adds a "current" suspense config that gets applied to all updates scheduled
during the current scope.
I suspect we might want to add other types of configurations to the "batch"
so I called it the "batch config".
This works across renderers/roots but they won't actually necessarily go
into the same batch.
* Add the suspenseConfig to all updates created during this scope
* Compute expiration time based on the timeout of the suspense config
* Track if there was a processed suspenseConfig this render pass
We'll use this info to suspend a commit for longer when necessary.
* Mark suspended states that should be avoided as a separate flag
This lets us track which renders we want to suspend for a short time vs
a longer time if possible.
* Suspend until the full expiration time if something asked to suspend
* Reenable an old test that we can now repro again
* Suspend the commit even if it is complete if there is a minimum delay
This can be used to implement spinners that don't flicker if the data
and rendering is really fast.
* Default timeoutMs to low pri expiration if not provided
This is a required argument in the type signature but people may not
supply it and this is a user facing object.
* Rename to withSuspenseConfig and drop the default config
This allow opting out of suspending in some nested scope.
A lot of time when you use this function you'll use it with high level
helpers. Those helpers often want to accept some additional configuration
for suspense and if it should suspend at all. The easiest way is to just
have the api accept null or a suspense config and pass it through. However,
then you have to remember that calling suspendIfNeeded has a default.
It gets simpler by just saying tat you can pass the config. You can have
your own default in user space.
* Track the largest suspense config expiration separately
This ensures that if we've scheduled lower pri work that doesn't have a
suspenseConfig, we don't consider its expiration as the timeout.
* Add basic tests for functionality using each update mechanism
* Fix issue when newly created avoided boundary doesn't suspend with delay
* Add test for loading indicator with minLoadingDurationMs option
* 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
Uses a dynamic flag in www's test renderer build so we can condtionally
disable the passive effects bugfix. Matches the dynamic flag used in
the www React DOM build.
PR #15650 is a bugfix but it's technically a semantic change that could
cause regressions. I don't think it will be an issue, since the
previous behavior was both broken and incoherent, but out of an
abundance of caution, let's wrap it in a flag so we can easily revert
it if necessary.
* 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.
https://webpack.js.org/plugins/define-plugin/
Webpack's DefinePlugin has the ability to replace `typeof expr` to a constant in compile-time, which should lead to better dead-code-elimination.