This commit also adds explicit index.stable and index.experimental forks to the react-is package so that we can avoid exporting references to SuspenseList in a stable release.
* The exported '<React.StrictMode>' tag remains the same and opts legacy subtrees into strict mode level one ('mode == StrictModeL1'). This mode enables DEV-only double rendering, double component lifecycles, string ref warnings, legacy context warnings, etc. The primary purpose of this mode is to help detected render phase side effects. No new behavior. Roots created with experimental 'createRoot' and 'createBlockingRoot' APIs will also (for now) continue to default to strict mode level 1.
In a subsequent commit I will add support for a 'level' attribute on the '<React.StrictMode>' tag (as well as a new option supported by ). This will be the way to opt into strict mode level 2 ('mode == StrictModeL2'). This mode will enable DEV-only double invoking of effects on initial mount. This will simulate future Offscreen API semantics for trees being mounted, then hidden, and then shown again. The primary purpose of this mode is to enable applications to prepare for compatibility with the new Offscreen API (more information to follow shortly).
For now, this commit changes no public facing behavior. The only mechanism for opting into strict mode level 2 is the pre-existing 'enableDoubleInvokingEffects' feature flag (only enabled within Facebook for now).
* Renamed strict mode constants
StrictModeL1 -> StrictLegacyMode and StrictModeL2 -> StrictEffectsMode
* Renamed tests
* Split strict effects mode into two flags
One flag ('enableStrictEffects') enables strict mode level 2. It is similar to 'debugRenderPhaseSideEffectsForStrictMode' which enables srtict mode level 1.
The second flag ('createRootStrictEffectsByDefault') controls the default strict mode level for 'createRoot' trees. For now, all 'createRoot' trees remain level 1 by default. We will experiment with level 2 within Facebook.
This is a prerequisite for adding a configurable option to 'createRoot' that enables choosing a different StrictMode level than the default.
* Add StrictMode 'unstable_level' prop and createRoot 'unstable_strictModeLevel' option
New StrictMode 'unstable_level' prop allows specifying which level of strict mode to use. If no level attribute is specified, StrictLegacyMode will be used to maintain backwards compatibility. Otherwise the following is true:
* Level 0 does nothing
* Level 1 selects StrictLegacyMode
* Level 2 selects StrictEffectsMode (which includes StrictLegacyMode)
Levels can be increased with nesting (0 -> 1 -> 2) but not decreased.
This commit also adds a new 'unstable_strictModeLevel' option to the createRoot and createBatchedRoot APIs. This option can be used to override default behavior to increase or decrease the StrictMode level of the root.
A subsequent commit will add additional DEV warnings:
* If a nested StrictMode tag attempts to explicitly decrease the level
* If a level attribute changes in an update
Use the pre-built scheduler (which includes a check for 'window' being defined in order to load the right scheduler implementation) rather than just directly importing a version of the scheduler that relies on window. Since the scheduling profiler's code runs partially in a web worker, it can't rely on window.
This commit changes scheduling profiler marks from a format like '--schedule-render-1' to '--schedule-render-1-Sync' (where 1 is the numeric value of the Sync lane). This will enable the profiler itself to show more meaningful labels for updates and render work.
The commit also refactors and adds additional tests for the scheduling profiler package.
It also updates the preprocessor to 'support' instant events. These are no-ops for us, but adding recognition of the event type will prevent profiles imported from e.g. Chrome Canary from throwing with an 'unrecognized event' error. (This will resolve issue #20767.)
With this change, if a node is a Fabric node, we route the setJSResponder call to FabricUIManager. Native counterpart is already landed. Tested internally as D26241364.
* Restore inspect-element bridge optimizations
When the new Suspense cache was integrated (so that startTransition could be used) I removed a couple of optimizations between the backend and frontend that reduced bridge traffic when e.g. dehydrated paths were inspected for elements that had not rendered since previously inspected. This commit re-adds those optimizations as well as an additional test with a bug fix that I noticed while reading the backend code.
There are two remaining TODO items as of this commit:
- Make inspected element edits and deletes also use transition API
- Don't over-eagerly refresh the cache in our ping-for-updates handler
I will addres both in subsequent commits.
* Poll for update only refreshes cache when there's an update
* Added inline comment
* Move direct port access into a function
* Fork based on presence of setImmediate
* Copy SchedulerDOM-test into another file
* Change the new test to use shimmed setImmediate
* Clarify comment
* Fix test to work with existing feature detection
* Add flags
* Disable OSS flag and skip tests
* Use VARIANT to reenable tests
* lol
Because we don't cancel synchronous tasks, sometimes more than one
synchronous task ends up being scheduled. This is an artifact of the
fact that we have two different lanes that schedule sync tasks: discrete
and sync. So what can happen is that a discrete update gets scheduled,
then a sync update right after that. Because sync is encoded as higher
priority than discrete, we schedule a second sync task. And since we
don't cancel the first one, there are now two separate sync tasks.
As a next step, what we should do is merge InputDiscreteLane with
SyncLane, then (I believe) this extra bailout wouldn't be necessary,
because there's nothing higher priority than sync that would cause us to
cancel it. Though we may want to add logging to be sure.
When running the publish workflow, either via the command line or
via the daily cron job, we should use a constant SHA instead of
whatever happens to be at the head of the main branch at the time the
workflow is run.
The difference is subtle: currently, the SHA is read at runtime,
each time the workflow is run. With this change, the SHA is read right
before the workflow is created and passed in as a constant parameter.
In practical terms, this means if a workflow is re-run via the CircleCI
web UI, it will always re-run using the same commit SHA as the original
workflow, instead of fetching the latest SHA from GitHub, which may
have changed.
Also avoids a race condition where the head SHA changes in between the
Next publish job and the Experimental publish job.
npm will sometimes fail if you try to concurrently publish two different
versions of the same package, even if they use different dist tags.
So instead of publishing to the Next and Experimental channels
simultaneously, we'll do them one after the other.
If we did want to speed up these publish workflows, we could paralellize
by package instead of by release channel.
* Add `supportsMicrotasks` to the host config
Only certain renderers support scheduling a microtask, so we need a
renderer specific flag that we can toggle. That way it's off for some
renderers and on for others.
I copied the approach we use for the other optional parts of the host
config, like persistent mode and test selectors.
Why isn't the feature flag sufficient?
The feature flag modules, confusingly, are not renderer-specific, at
least when running the our tests against the source files. They are
meant to correspond to a release channel, not a renderer, but we got
confused at some point and haven't cleaned it up.
For example, when we run `yarn test`, Jest loads the flags from the
default `ReactFeatureFlags.js` module, even when we import the React
Native renderer — but in the actual builds, we load a different feature
flag module, `ReactFeatureFlags.native-oss.js.` There's no way in our
current Jest load a different host config for each renderer, because
they all just import the same module. We should solve this by creating
separate Jest project for each renderer, so that the flags loaded when
running against source are the same ones that we use in the
compiled bundles.
The feature flag (`enableDiscreteMicrotasks`) still exists — it's used
to set the React DOM host config's `supportsMicrotasks` flag to `true`.
(Same for React Noop) The important part is that turning on the feature
flag does *not* affect the other renderers, like React Native.
The host config will likely outlive the feature flag, too, since the
feature flag only exists so we can gradually roll it out and measure the
impact in production; once we do, we'll remove it. Whereas the host
config flag may continue to be used to disable the discrete microtask
behavior for RN, because RN will likely use a native (non-JavaScript)
API to schedule its tasks.
* Add `supportsMicrotask` to react-reconciler README
* Warn if static flag is accidentally cleared
"Static" fiber flags are flags that are meant to exist for the lifetime
of a component. It's really important not to accidentally reset these,
because we use them to decide whether or not to perform some operation
on a tree (which we can do because they get bubbled via `subtreeFlags)`.
We've had several bugs that were caused by this mistake, so we actually
don't rely on static flags anywhere, yet. But we'd like to.
So let's roll out this warning and see if it fires anywhere. Once we
can confirm that there are no warnings, we can assume that it's safe
to start using static flags.
I did not wrap it behind a feature flag, because it's dev-only, and we
can use our internal warning filter to hide this from the console.
* Intentionally clear static flag to test warning
* ...and fix it again
(Except transitions and retries.)
The idea is that the only priorities that benefit from multiple parallel
updates are the ones that might suspend: transitions and retries. All
other priorities, including the ones that are interruptible like
Continuous and Idle, don't need multiple lanes because it's better to
batch everything together.
We don't always keep the reconciler forks in sync (otherwise it we
wouldn't have forked it) but during periods when they are meant to be in
sync, we use this job to confirm there are no differences.
* Parallelize Flow in CI
We added more host configs recently, and we run all the checks in
sequence, so sometimes Flow ends up being the slowest CI job.
This splits the job across multiple processes.
* Fix environment variable typo
Co-authored-by: Ricky <rickhanlonii@gmail.com>
Co-authored-by: Ricky <rickhanlonii@gmail.com>
The only difference between default updates and transition updates is
that default updates do not support suspended refreshes — they will
instantly display a fallback.
Co-authored-by: Rick Hanlon <rickhanlonii@gmail.com>
* Apply #20778 to new fork, too
* Fix tests that use runWithPriority
Where possible, I tried to rewrite in terms of an idiomatic API.
For DOM tests, we should be dispatching an event with the desired
priority level.
For Idle updates (very unstable feature), probably need an unstable
API like ReactDOM.unstable_IdleUpdates.
Some of these fixes are not great, but we can replace them once we've
landed the more of our planned changes to the layering between
Scheduler, the reconciler, and the renderer.
* Convert some old discrete tests to Hooks
I'm planning to copy paste so why not update them anyway.
* Copy paste discrete tests into another file
These are still using React events. I'll change that next.
* Convert the test to use native events
* Add the feature flag
* Add a host config method
* Wire it up to the work loop
* Export constants for third-party renderers
* Document for third-party renderers
Now that interleaved updates are added to a special queue, we no longer
need to shift them into their own lane. We can add to a lane that's
already in the middle of rendering without risk of tearing.
See #20615 for more background.
I've only changed this in the new fork, and only behind the
enableTransitionEntanglements flag.
Most of this commit involves updating tests. The "shift-to-a-new" lane
trick was intentionally used in a handful of tests where two or more
updates need to be scheduled in different lanes. Most of these tests
were written before `startTransition` existed, and all of them were
written before transitions were assigned arbitrary lanes.
So I ported these tests to use `startTransition` instead, which is the
idiomatic way to mark an update as parallel.
I didn't change the old fork at all. Writing these tests in such a way
that they also pass in the old fork actually revealed a few flaws in the
current implementation regarding interrupting a suspended refresh
transition early, which is a good reminder that we should be writing our
tests using idiomatic patterns as much as we possibly can.
* Land enableTransitionEntanglement changes
Leaving the flag though because I plan to reuse it for additional,
similar changes.
* Assign different lanes to consecutive transitions
Currently we always assign the same lane to all transitions. This means
if there are two pending transitions at the same time, neither
transition can finish until both can finish, even if they affect
completely separate parts of the UI.
The new approach is to assign a different lane to each consecutive
transition, by shifting the bit to the right each time. When we reach
the end of the bit range, we cycle back to the first bit. In practice,
this should mean that all transitions get their own dedicated lane,
unless we have more pending transitions than lanes, which should
be rare.
We retain our existing behavior of assigning the same lane to all
transitions within the same event. This is achieved by caching the first
lane assigned to a transition, then re-using that one until the next
React task, by which point the event must have finished. This preserves
the guarantee that all transition updates that result from a single
event will be consistent.
Because we have access to the artifacts in CI, we can read bundle sizes
directly from the filesystem, instead of the JSON files emitted by our
custom Rollup plugin.
This gives us some flexibility if we ever have artifacts that aren't
generated by Rollup, or if we rewrite our build script.
Personally, I also prefer to see the whole file path, instead of just
the name, because some of our names are repeated.
My immediate motivation, though, is because it gives us a way to merge
the separate "experimental" and "stable" size results. Instead
everything is reported in a single table and disambiguated by path.
I also added a section at the top that always displays the size impact
to certain critical bundles — right now, that's the React DOM production
bundles for each release channel. This section will also include any
size changes larger than 2%.
Below that is a section that is collapsed by default and includes all
size changes larger than 0.2%.