* Revert "Revert "Dispatch commands to both UIManagers from both renderers (#17211)" (#17232)"
This reverts commit d0fc0ba0a6.
* Clean up another __DEV__ warning check
* Replace all warning/lowPriWarning with console calls
* Replace console.warn/error with a custom wrapper at build time
* Fail the build for console.error/warn() where we can't read the stack
* prep for codemod
* prep warnings
* rename lint rules
* codemod for ifs
* shim www functions
* Handle more cases in the transform
* Thanks De Morgan
* Run the codemod
* Delete the transform
* Fix up confusing conditions manually
* Fix up www shims to match expected API
* Also check for low-pri warning in the lint rule
* Replace Babel plugin with an ESLint plugin
* Fix ESLint rule violations
* Move shared conditions higher
* Test formatting nits
* Tweak ESLint rule
* Bugfix: inside else branch, 'if' tests are not satisfactory
* Use a stricter check for exactly if (__DEV__)
This makes it easier to see what's going on and matches dominant style in the codebase.
* Fix remaining files after stricter check
* 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.
* Adding ReactNative.setNativeProps that takes a ref
* Adding test for components rendered with Fabric with Paper's setNativeProps
* Fixing flow types
* Fix prettier
* Rename ReactNativeSetNativeProps.js to be more general
Whenever we do this, Rollup needs to materialize this as an object.
This causes it to also add the Babel compatibility property which is
unnecessary bloat. However, since when we use these, we leak the object
this often also deopts any compiler optimizations.
If we really need an object we should export default an object.
Currently there is an exception for DOMTopLevelEventTypes since
listing out the imports is a PITA and it doesn't escape so it should
get properly inlined. We should probably move to a different pattern
to avoid this for consistency though.
* Deprecate findDOMNode in StrictMode
There are two scenarios. One is that we pass a component instance that is
already in strict mode or the node that we find is in strict mode if
an outer component renders into strict mode.
I use a separate method findHostInstanceWithWarning for this so that
a) I can pass the method name (findDOMNode/findNodeHandle).
b) Can ignore this warning in React Native mixins/NativeComponent that use this helper.
I don't want to expose the fiber to the renderers themselves.
* Replace some namespace imports
* Simplify the controlled component injection
* Simplify the batching injection
* Simplify the component tree injection
* Use %s in the console calls
* Add shared/warningWithStack
* Convert some warning callsites to warningWithStack
* Use warningInStack in shared utilities and remove unnecessary checks
* Replace more warning() calls with warningWithStack()
* Fixes after rebase + use warningWithStack in react
* Make warning have stack by default; warningWithoutStack opts out
* Forbid builds that may not use internals
* Revert newly added stacks
I changed my mind and want to keep this PR without functional changes. So we won't "fix" any warnings that are already missing stacks. We'll do it in follow-ups instead.
* Fix silly find/replace mistake
* Reorder imports
* Add protection against warning argument count mismatches
* Address review
* Fix getComponentName() for types with nested $$typeof
* Temporarily remove Profiler ID from messages
* Change getComponentName() signature to take just type
It doesn't actually need the whole Fiber.
* Remove getComponentName() forks in isomorphic and SSR
* Remove unnecessary .type access where we already have a type
* Remove unused type
We need a different "component tree" thingy for Fabric.
A lot of this doesn't really make much sense in a persistent world but
currently we can't dispatch events to memoizedProps on a Fiber since
they're pooled. Also, it's unclear what the semantics should be when we
dispatch an event that happened when the old props were in effect but now
we have new props already.
This implementation tries to use the last committed props but also fails
at that because we don't have a commit hook in the persistent mode.
However, at least it doesn't crash when dispatching. :)
* Extract base Jest config
This makes it easier to change the source config without affecting the build test config.
* Statically import the host config
This changes react-reconciler to import HostConfig instead of getting it through a function argument.
Rather than start with packages like ReactDOM that want to inline it, I started with React Noop and ensured that *custom* renderers using react-reconciler package still work. To do this, I'm making HostConfig module in the reconciler look at a global variable by default (which, in case of the react-reconciler npm package, ends up being the host config argument in the top-level scope).
This is still very broken.
* Add scaffolding for importing an inlined renderer
* Fix the build
* ES exports for renderer methods
* ES modules for host configs
* Remove closures from the reconciler
* Check each renderer's config with Flow
* Fix uncovered Flow issue
We know nextHydratableInstance doesn't get mutated inside this function, but Flow doesn't so it thinks it may be null.
Help Flow.
* Prettier
* Get rid of enable*Reconciler flags
They are not as useful anymore because for almost all cases (except third party renderers) we *know* whether it supports mutation or persistence.
This refactoring means react-reconciler and react-reconciler/persistent third-party packages now ship the same thing.
Not ideal, but this seems worth how simpler the code becomes. We can later look into addressing it by having a single toggle instead.
* Prettier again
* Fix Flow config creation issue
* Fix imprecise Flow typing
* Revert accidental changes
* Move findNodeHandle into the renderers and use instantiation
This is just like ReactDOM does it. This also lets us get rid of injection
for findNodeHandle. Instead I move NativeMethodsMixin and ReactNativeComponent
to use instantiation.
* Refactor findHostInstance
The reconciler shouldn't expose the Fiber data structure. We should pass
the component instance to the reconciler, since the reconciler is the
thing that is supposed to be instancemap aware.
* Fix devtools injection
* Move view config registry to shims
This ensures that both Fabric and RN renderers share the same view config
registry since it is stateful.
I had to duplicate in the mocks for testing.
* Move createReactNativeComponentClass to shims and delete internal usage
Since createReactNativeComponentClass is just an alias for the register
there's no need to bundle it. This file should probably just move back
to RN too.
We already have one stateful module that contains all the view config.
We might as well store the event types there too. That way the shared
state is compartmentalized (and I can move it out in a follow up PR).
The view config registry also already has an appropriate place to call
processEventTypes so now we no longer have to do it in RN.
Will follow up with a PR to RN to remove that call.
These don't make much sense in Fabric, since Fabric will be async by default only.
And unmount+remove container is a sketchy API we should remove so we might
as well make sure modern containers enforce that.
This doesn't actually need to share any state because it goes through
the instance to the fiber structure. Since Fabric is on the same version
as RN, calling it on either renderer works.
There are no plans to enable async in the old renderer. In the new renderer
it only really makes sense to do from the main thread and probably from
native since it'll have to yield to native first.
* Don't expose ReactGlobalSharedState on React Native renderer
We should just go through the "react" package if need access to this one.
Removed the dependencies in React Native.
* No longer used by InspectorUtils
* Updates inside controlled events (onChange) are sync even in async mode
This guarantees the DOM is in a consistent state before we yield back
to the browser.
We'll need to figure out a separate strategy for other
interactive events.
* Don't rely on flushing behavior of public batchedUpdates implementation
Flush work as an explicit step at the end of the event, right before
restoring controlled state.
* Interactive updates
At the beginning of an interactive browser event (events that fire as
the result of a user interaction, like a click), check for pending
updates that were scheduled in a previous interactive event. Flush the
pending updates synchronously so that the event handlers are up-to-date
before responding to the current event.
We now have three classes of events:
- Controlled events. Updates are always flushed synchronously.
- Interactive events. Updates are async, unless another a subsequent
event is fired before it can complete, as described above. They are
also slightly higher priority than a normal async update.
- Non-interactive events. These are treated as normal, low-priority
async updates.
* Flush lowest pending interactive update time
Accounts for case when multiple interactive updates are scheduled at
different priorities. This can happen when an interactive event is
dispatched inside an async subtree, and there's an event handler on
an ancestor that is outside the subtree.
* Update comment about restoring controlled components