Sometimes the status of the `build` job is not in the first page of
the `/statuses` endpoint. The combined `/status` endpoint consolidates
the entries, though, so it always appears there.
* Refactor component search to prepare for deeper traversals
* Register HOCs with intermediate results
* Register components that are used as JSX types
* Add integration test skeleton
The integration test combines testing runtime together with the Babel plugin. It's a bit harder to debug because multiple things can go wrong, but it helps us build confidence that specific scenarios work well.
* Add HOC integration test and fix conflict with JSX transform
* Infer usage from createElement too
This helps us avoid dependency on the plugin order.
* Remove outdated comments
* Wrap tests in __DEV__
* Support export default hoc(...) for anonymous functions
* Fix test indentation
* Fix comment typo
* Use named function for test as this case is more important
* 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.
* [react-native] Use path-based imports instead of Haste for the RN renderer
To move React Native to standard path-based imports instead of Haste, the RN renderer that is generated from the code in this repo needs to use path-based imports as well since the generated code is vendored by RN. This commit makes it so the interface between the generated renderers and RN does not rely on Haste and instead uses a private interface explicitly defined by RN. This inverts control of the abstraction so that RN decides the internals to export rather than React deciding what to import.
On RN's side, a new module named `react-native/Libraries/ReactPrivate/ReactNativePrivateInterface` explicitly exports the modules used by the renderers in this repo. (There is also a private module for InitializeCore so that we can import it just for the side effects.) On React's side, the various renderer modules access RN internals through the explicit private interface.
The Rollup configuration becomes slimmer since the only external package is now `react-native`, and the individual modules are instead listed out in `ReactNativePrivateInterface`.
Task description: https://github.com/facebook/react-native/issues/24770
Sister RN PR (needs to land before this one): https://github.com/facebook/react-native/pull/24782
Test Plan: Ran unit tests and Flow in this repo. Generated the renderers and manually copied them over to the RN repo. Ran the RN tests and launched the RNTester app.
* Access natively defined "nativeFabricUIManager" instead of importing it
Some places in the Fabric renderers access `nativeFabricUIManager` (a natively defined global) instead of importing UIManager. While this is coupling across repos that depends on the timing of events, it is necessary until we have a way to defer top-level imports to run after `nativeFabricUIManager` is defined. So for consistency we use `nativeFabricUIManager` everywhere (see the comment in https://github.com/facebook/react/pull/15604#pullrequestreview-236842223 for more context).
The sizebot scrapes the GitHub `/statuses` endpoint to get the lastest
CircleCI build number for master, in order to fetch the bundle size
info for that build, which are stored as build artifacts. (There's gotta
be a better way to do this, but that's what we have for now.) This
updates the script to match the name of the updated CircleCI job that
generates the bundle sizes.
* Add initial Babel plugin implementation
* Register exported functions
* Fix missing declarations
Always declare them at the bottom and rely on hoisting.
* Remove unused code
* Don't pass filename to tests
I've decided for now that the plugin doesn't need filename, and it will be handled by module runtime integration instead.
* Fix bugs
* Coalesce variable declarations
Updates the CircleCI config to use the workflows features to run jobs in
parallel, instead of the `parallelism` option. This change alone doesn't
improve the overall build time much, since almost all of the total time
is spent running the Rollup script, which runs entirely sequentially.
But it does improve reporting, and should make it easier to add
additional parallel jobs in the future.
* Fix missing return pointer assignment
I found a bug using the fuzz tester that manifested as incorrect
ordering of children in the host tree, but whose root cause was a
missing `return` pointer assignment on a work-in-progress fiber.
Usually return pointers are set during reconciliation
(`reconcileChildFibers`) but this particular assignment happens inside
the custom reconciliation implementation used by Suspense boundaries.
I would not be surprised if there were similar bugs related to incorrect
return pointers. You're supposed to update the return pointer
whenever a work-in-progress fiber is created, but there's nothing in
the contract of the `createFiber` or `createWorkInProgress` function
that implies this. I propose that we update their signatures to accept
the return fiber as an argument. I will do this in a follow-up.
In this commit, I rearranged `updateSuspenseComponent` slightly so that
every call to `createWorkInProgress` or a `createFiber*` function is
immediately followed by a return pointer assignment.
I hardcoded the fuzz test case that surfaced the bug.
* Update all progressed children in list
`progressedPrimaryChild` is a list, not a single fiber. Need to iterate
through every child and update their return pointers.
* Add a stub for React Fresh Babel plugin package
* Move ReactFresh-test into ReactFresh top level directory
* Add a stub for React Fresh Runtime entry point
* Extract Fresh runtime from tests into its entry point
* Don't traverse children when hot reloading needs a remount
If we're gonna remount that tree anyway, there is no use in traversing its children beforehand.
* Add a test verifying hot reload batches updates
Otherwise there is a risk of it being super slow due to cascades.
* Add a minimal failing test for hot reload
* Set up scaffolding for React Fresh
* Consider type family when comparing elementType
Rendering an element with stale type should not cause it to remount.
We only do this for FunctionComponent tag since checking is unnecessary for classes or host components.
* Add support for forwardRef()
Initially I thought I would compare families of inner .render functions.
However, there is a corner case where this can create false positives. Such as when you forwardRef(X) the same X twice. Those are supposed to be distinct. But if we compare .render functions, we wouldn't be able to distinguish them after first reload.
It seems safer to rely on explicit registration for those. This should be easy when forwardRef() call is in the same file, and usually it would be. For cases like HOCs and style.div`...` factories that return forwardRef(), we could have the __register__ helper itself "dig deeper" and register the inner function.
* Show how forwardRef inner identity can be inferred
The __register__ implementation can read the inner identity itself.
* Add missing __DEV__ to tests
* Add support for memo() (without fixing bailouts)
This adds rudimentary support for memo components. However, we don't actually skip bailouts yet, so this is not very useful by itself alone. Tests have TODOs that we need to remove after bailout skipping is done.
* Refactor type comparison for clarity
* Hot update shouldn't re-render ancestor components unnecessarily
My code had a bug where it checked for a wrong thing in a wrong set, leading us to always re-render.
This fixes the checks so that we only schedule updates for things that were actually edited.
* Add test coverage for memo(fn, areEqual)
* Explicitly skip bailouts for hot reloading fibers
This forces even memo() with shallow comparison to re-render on hot update.
* Refactor scheduling update to reduce duplication
* Remove unused variable in test
* Don't check presence in a set while not hot reloading
* Make scheduleHotUpdate() take named arguments
* Don't keep unedited component types in the type => family map
It's unnecessary because if they haven't been edited, there's no special reconciliation logic.
* Add signatures that force remounting
Signatures let us force a remount of a type even if from React's point of view, type is the same.
A type has one current signature. If that signature changes during next hot update, all Fibers with that type should be deleted and remounted.
We do this by mutating elementType scheduling a parent.
This will be handy to force remount of mismatching Hooks, as well as failed error boundaries.
For this to fully work, we'll need to add a way to skip built-in bailouts for all Fiber types. This will be the most invasive and annoying change. I did it for HostRoot in this PR but there's more. I'll add an automated test case that catches the missing bailout bailouts.
* Support forced remounting for all component types
This teaches all parent component types to remount their child if necessary.
It also adds tests for them.
* Remount effects while preserving state for hot reloaded components
This makes sure that changes to *code* always propagate.
It can break components that aren't resilient to useEffect over-firing, but that seems like a good constraint since you might need to add a dependency later anyway, and this helps avoid coding yourself into the corner.
* Add missing __DEV__ blocks to tests
* Fix unused variables in tests
* Remove outdated TODO
* Expose scheduleHotUpdate directly
* Inline isCompatibleType
* Run one check per component for invalidating deps
This also makes the bailouts more targeted--no need to remount useEffect for a parent component of remounted fiber.
* Resolve .type early
This moves resolving to set up the right .type early instead of doing this before render.
A bit more future-proof in case we want to restructure the begin phase later.
ForwardRef is special because its type is a wrapper but it can be hot reloaded by itself.
So we have a special overload for it that reconstucts the wrapper type if needed.
* Add a Suspense todo
* Use current.type !== workInProgress.type for ignoring deps
This gets rid of one of the sets.
* Use workInProgress.type !== current.type check for force re-render
We still use a set for forced remount though.
* Use wip.type !== current.type check in more places
This also disables the remounting tests. They need a separate approach.
* Use a dedicated remount mechanism
* Add a test for offscreen trees
It has a TODO because it seems like offscreen updates are incorrectly applied too soon.
* Enable offscreen test now that it is fixed
* Fix corner cases in the new remounting mechanism
* Remount failed error boundaries on hot reload
* Fix test now that act() flushes
This test is manual so I don't actually want act here.
* Nits
* Add comments
* 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.
* Failing test for false positive warning
* Flush passive effects before discrete events
Currently, we check for pending passive effects inside the `setState`
method before we add additional updates to the queue, in case those
pending effects also add things to the queue.
However, the `setState` method is too late, because the event that
caused the update might not have ever fired had the passive effects
flushed before we got there.
This is the same as the discrete/serial events problem. When a serial
update comes in, and there's already a pending serial update, we have to
do it before we call the user-provided event handlers. Because the event
handlers themselves might change as a result of the pending update.
This commit moves the `flushPassiveEffects` call to before the discrete
event handlers are called, and removes it from the `setState` method.
Non-discrete events will not cause passive effects to flush, which is
fine, since by definition they are not order dependent.
If React finishes rendering a tree, delays committing it (e.g.
Suspense), then subsequently starts over or renders a new tree, the
pending tree is no longer valid. That's because rendering a new work-in
progress mutates the old one in place.
The current structure of the work loop makes this hard to reason about
because, although `renderRoot` and `commitRoot` are separate functions,
they can't be interleaved. If they are interleaved by accident, it
either results in inconsistent render output or invariant violations
that are hard to debug.
This commit adds an invariant that throws if the new tree is the same as
the old one. This won't prevent all bugs of this class, but it should
catch the most common kind.
To implement the invariant, I store the finished tree on a field on the
root. We already had a field for this, but it was only being used for
the unstable `createBatch` feature.
A more rigorous way to address this type of problem could be to unify
`renderRoot` and `commitRoot` into a single function, so that it's
harder to accidentally interleave the two phases. I plan to do something
like this in a follow-up.
* Add ReactDOM.unstable_createSyncRoot
- `ReactDOM.unstable_createRoot` creates a Concurrent Mode root.
- `ReactDOM.unstable_createSyncRoot` creates a Batched Mode root. It
does not support `createBatch`.
- `ReactDOM.render` creates a Legacy Mode root. It will eventually be
deprecated and possibly moved to a separate entry point, like
`react-dom/legacy`.
* Rename internal ReactRoot types
* 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.