[CS] Persistent Updates (#11260)
* Update build size
* [CS] Clone container instead of new root concept
The extra "root" concept is kind of unnecessary. Instead of having a
mutable container even in the persistent mode, I'll instead make the
container be immutable too and be cloned. Then the "commit" just becomes
swapping the previous container for the new one.
* Change the signature or persistence again
We may need to clone without any updates, e.g. when the children are changed.
Passing in the previous node is not enough to recycle since it won't have the
up-to-date props and children. It's really only useful to for allocation pooling.
* Implement persistent updates
This forks the update path for host fibers. For mutation mode we mark
them as having an effect. For persistence mode, we clone the stateNode with
new props/children.
Next I'll do HostRoot and HostPortal.
* Refine protocol into a complete and commit phase
finalizeContainerChildren will get called at the complete phase.
replaceContainer will get called at commit.
Also, drop the keepChildren flag. We'll never keep children as we'll never
update a container if none of the children has changed.
* Implement persistent updates of roots and portals
These are both "containers". Normally we rely on placement/deletion effects
to deal with insertions into the containers. In the persistent mode we need
to clone the container and append all the changed children to it.
I needed somewhere to store these new containers before they're committed
so I added another field.
* Commit persistent work at the end by swapping out the container
* Unify cloneOrRecycle
Originally I tried to make the recyclable instance nullable but Flow didn't
like that and it's kind of sketchy since the instance type might not be
nullable.
However, the real difference which one we call is depending on whether they
are equal. We can just offload that to the renderer. Most of them won't
need to know about this at all since they'll always clone or just create
new.
The ones that do know now have to be careful to compare them so they don't
reuse an existing instance but that's probably fine to simplify the
implementation and API.
* Add persistent noop renderer for testing
* Add basic persistent tree test
* Test bail out
This adds a test for bailouts. This revealed a subtle bug. We don't set the
return pointer when stepping into newly created fibers because there
can only be one. However, since I'm reusing this mechanism for persistent
updates, I'll need to set the return pointer because a bailed out tree
won't have the right return pointer.
* Test persistent text nodes
Found another bug.
* Add persistent portal test
This creates a bit of an unfortunate feature testing in the unmount
branch.
That's because we want to trigger nested host deletions in portals in the
mutation mode.
* Don't consider container when determining portal identity
Basically, we can't use the container to determine if we should keep
identity and update an existing portal instead of recreate it. Because
for persistent containers, there is no permanent identity.
This makes it kind of strange to even use portals in this mode. It's
probably more ideal to have another concept that has permanent identity
rather than trying to swap out containers.
* Clear portals when the portal is deleted
When a portal gets deleted we need to create a new empty container and
replace the current one with the empty one.
* Add renderer mode flags for dead code elimination
* Simplify ReactNoop fix
* Add new type to the host config for persistent configs
We need container to stay as the persistent identity of the root atom.
So that we can refer to portals over time.
Instead, I'll introduce a new type just to temporarily hold the children
of a container until they're ready to be committed into the permanent
container. Essentially, this is just a fancy array that is not an array
so that the host can choose data structure/allocation for it.
* Implement new hooks
Now containers are singletons and instead their children swap. That way
portals can use the container as part of their identity again.
* Update build size and error codes
* Address comment
* Move new files to new location
2017-10-19 09:28:23 +08:00
|
|
|
/**
|
2018-09-08 06:11:23 +08:00
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
[CS] Persistent Updates (#11260)
* Update build size
* [CS] Clone container instead of new root concept
The extra "root" concept is kind of unnecessary. Instead of having a
mutable container even in the persistent mode, I'll instead make the
container be immutable too and be cloned. Then the "commit" just becomes
swapping the previous container for the new one.
* Change the signature or persistence again
We may need to clone without any updates, e.g. when the children are changed.
Passing in the previous node is not enough to recycle since it won't have the
up-to-date props and children. It's really only useful to for allocation pooling.
* Implement persistent updates
This forks the update path for host fibers. For mutation mode we mark
them as having an effect. For persistence mode, we clone the stateNode with
new props/children.
Next I'll do HostRoot and HostPortal.
* Refine protocol into a complete and commit phase
finalizeContainerChildren will get called at the complete phase.
replaceContainer will get called at commit.
Also, drop the keepChildren flag. We'll never keep children as we'll never
update a container if none of the children has changed.
* Implement persistent updates of roots and portals
These are both "containers". Normally we rely on placement/deletion effects
to deal with insertions into the containers. In the persistent mode we need
to clone the container and append all the changed children to it.
I needed somewhere to store these new containers before they're committed
so I added another field.
* Commit persistent work at the end by swapping out the container
* Unify cloneOrRecycle
Originally I tried to make the recyclable instance nullable but Flow didn't
like that and it's kind of sketchy since the instance type might not be
nullable.
However, the real difference which one we call is depending on whether they
are equal. We can just offload that to the renderer. Most of them won't
need to know about this at all since they'll always clone or just create
new.
The ones that do know now have to be careful to compare them so they don't
reuse an existing instance but that's probably fine to simplify the
implementation and API.
* Add persistent noop renderer for testing
* Add basic persistent tree test
* Test bail out
This adds a test for bailouts. This revealed a subtle bug. We don't set the
return pointer when stepping into newly created fibers because there
can only be one. However, since I'm reusing this mechanism for persistent
updates, I'll need to set the return pointer because a bailed out tree
won't have the right return pointer.
* Test persistent text nodes
Found another bug.
* Add persistent portal test
This creates a bit of an unfortunate feature testing in the unmount
branch.
That's because we want to trigger nested host deletions in portals in the
mutation mode.
* Don't consider container when determining portal identity
Basically, we can't use the container to determine if we should keep
identity and update an existing portal instead of recreate it. Because
for persistent containers, there is no permanent identity.
This makes it kind of strange to even use portals in this mode. It's
probably more ideal to have another concept that has permanent identity
rather than trying to swap out containers.
* Clear portals when the portal is deleted
When a portal gets deleted we need to create a new empty container and
replace the current one with the empty one.
* Add renderer mode flags for dead code elimination
* Simplify ReactNoop fix
* Add new type to the host config for persistent configs
We need container to stay as the persistent identity of the root atom.
So that we can refer to portals over time.
Instead, I'll introduce a new type just to temporarily hold the children
of a container until they're ready to be committed into the permanent
container. Essentially, this is just a fancy array that is not an array
so that the host can choose data structure/allocation for it.
* Implement new hooks
Now containers are singletons and instead their children swap. That way
portals can use the container as part of their identity again.
* Update build size and error codes
* Address comment
* Move new files to new location
2017-10-19 09:28:23 +08:00
|
|
|
*
|
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
|
*
|
|
|
|
|
* @flow
|
|
|
|
|
*/
|
|
|
|
|
|
2017-11-06 22:14:48 +08:00
|
|
|
import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
2020-02-14 04:33:53 +08:00
|
|
|
import typeof * as ExportsType from './ReactFeatureFlags.native-oss';
|
[CS] Persistent Updates (#11260)
* Update build size
* [CS] Clone container instead of new root concept
The extra "root" concept is kind of unnecessary. Instead of having a
mutable container even in the persistent mode, I'll instead make the
container be immutable too and be cloned. Then the "commit" just becomes
swapping the previous container for the new one.
* Change the signature or persistence again
We may need to clone without any updates, e.g. when the children are changed.
Passing in the previous node is not enough to recycle since it won't have the
up-to-date props and children. It's really only useful to for allocation pooling.
* Implement persistent updates
This forks the update path for host fibers. For mutation mode we mark
them as having an effect. For persistence mode, we clone the stateNode with
new props/children.
Next I'll do HostRoot and HostPortal.
* Refine protocol into a complete and commit phase
finalizeContainerChildren will get called at the complete phase.
replaceContainer will get called at commit.
Also, drop the keepChildren flag. We'll never keep children as we'll never
update a container if none of the children has changed.
* Implement persistent updates of roots and portals
These are both "containers". Normally we rely on placement/deletion effects
to deal with insertions into the containers. In the persistent mode we need
to clone the container and append all the changed children to it.
I needed somewhere to store these new containers before they're committed
so I added another field.
* Commit persistent work at the end by swapping out the container
* Unify cloneOrRecycle
Originally I tried to make the recyclable instance nullable but Flow didn't
like that and it's kind of sketchy since the instance type might not be
nullable.
However, the real difference which one we call is depending on whether they
are equal. We can just offload that to the renderer. Most of them won't
need to know about this at all since they'll always clone or just create
new.
The ones that do know now have to be careful to compare them so they don't
reuse an existing instance but that's probably fine to simplify the
implementation and API.
* Add persistent noop renderer for testing
* Add basic persistent tree test
* Test bail out
This adds a test for bailouts. This revealed a subtle bug. We don't set the
return pointer when stepping into newly created fibers because there
can only be one. However, since I'm reusing this mechanism for persistent
updates, I'll need to set the return pointer because a bailed out tree
won't have the right return pointer.
* Test persistent text nodes
Found another bug.
* Add persistent portal test
This creates a bit of an unfortunate feature testing in the unmount
branch.
That's because we want to trigger nested host deletions in portals in the
mutation mode.
* Don't consider container when determining portal identity
Basically, we can't use the container to determine if we should keep
identity and update an existing portal instead of recreate it. Because
for persistent containers, there is no permanent identity.
This makes it kind of strange to even use portals in this mode. It's
probably more ideal to have another concept that has permanent identity
rather than trying to swap out containers.
* Clear portals when the portal is deleted
When a portal gets deleted we need to create a new empty container and
replace the current one with the empty one.
* Add renderer mode flags for dead code elimination
* Simplify ReactNoop fix
* Add new type to the host config for persistent configs
We need container to stay as the persistent identity of the root atom.
So that we can refer to portals over time.
Instead, I'll introduce a new type just to temporarily hold the children
of a container until they're ready to be committed into the permanent
container. Essentially, this is just a fancy array that is not an array
so that the host can choose data structure/allocation for it.
* Implement new hooks
Now containers are singletons and instead their children swap. That way
portals can use the container as part of their identity again.
* Update build size and error codes
* Address comment
* Move new files to new location
2017-10-19 09:28:23 +08:00
|
|
|
|
2018-01-26 06:30:53 +08:00
|
|
|
export const debugRenderPhaseSideEffectsForStrictMode = false;
|
2020-04-16 10:10:15 +08:00
|
|
|
export const enableDebugTracing = false;
|
2020-07-08 22:36:02 +08:00
|
|
|
export const enableSchedulingProfiler = false;
|
2018-04-19 04:16:50 +08:00
|
|
|
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
|
2019-03-28 07:30:49 +08:00
|
|
|
export const warnAboutDeprecatedLifecycles = true;
|
2018-06-12 04:16:27 +08:00
|
|
|
export const enableProfilerTimer = __PROFILE__;
|
2021-05-26 05:32:14 +08:00
|
|
|
export const enableProfilerCommitHooks = __PROFILE__;
|
|
|
|
|
export const enableProfilerNestedUpdatePhase = __PROFILE__;
|
2020-11-12 22:31:27 +08:00
|
|
|
export const enableProfilerNestedUpdateScheduledHook = false;
|
2021-05-26 05:41:19 +08:00
|
|
|
export const enableUpdaterTracking = __PROFILE__;
|
2020-12-03 11:44:56 +08:00
|
|
|
export const enableCache = false;
|
2022-03-21 11:41:02 +08:00
|
|
|
export const enableCacheElement = false;
|
2019-03-12 07:39:49 +08:00
|
|
|
export const disableJavaScriptURLs = false;
|
2022-02-18 05:44:22 +08:00
|
|
|
export const disableCommentsAsDOMContainers = true;
|
2018-09-13 02:29:23 +08:00
|
|
|
export const disableInputAttributeSyncing = false;
|
2018-12-07 05:57:23 +08:00
|
|
|
export const enableSchedulerDebugging = false;
|
2019-08-29 19:06:51 +08:00
|
|
|
export const enableScopeAPI = false;
|
2020-05-13 02:01:12 +08:00
|
|
|
export const enableCreateEventHandleAPI = false;
|
2019-07-24 08:13:46 +08:00
|
|
|
export const enableSuspenseCallback = false;
|
2019-07-26 06:44:03 +08:00
|
|
|
export const warnAboutDefaultPropsOnFunctionComponents = false;
|
2019-08-07 15:10:19 +08:00
|
|
|
export const warnAboutStringRefs = false;
|
2019-08-02 08:21:32 +08:00
|
|
|
export const disableLegacyContext = false;
|
2019-08-03 08:52:32 +08:00
|
|
|
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
|
2019-09-16 20:43:22 +08:00
|
|
|
export const enableTrustedTypesIntegration = false;
|
2020-01-20 23:12:30 +08:00
|
|
|
export const disableTextareaChildren = false;
|
2020-03-07 02:46:32 +08:00
|
|
|
export const disableModulePatternComponents = false;
|
2020-02-20 19:30:04 +08:00
|
|
|
export const warnAboutSpreadingKeyToJSX = false;
|
2021-09-21 03:44:48 +08:00
|
|
|
export const enableSuspenseAvoidThisFallback = false;
|
2022-02-02 07:22:04 +08:00
|
|
|
export const enableSuspenseAvoidThisFallbackFizz = false;
|
2022-03-08 14:33:52 +08:00
|
|
|
export const enableCPUSuspense = false;
|
experimental_use(promise) (#25084)
* Internal `act`: Unwrapping resolved promises
This update our internal implementation of `act` to support React's new
behavior for unwrapping promises. Like we did with Scheduler, when
something suspends, it will yield to the main thread so the microtasks
can run, then continue in a new task.
I need to implement the same behavior in the public version of `act`,
but there are some additional considerations so I'll do that in a
separate commit.
* Move throwException to after work loop resumes
throwException is the function that finds the nearest boundary and
schedules it for a second render pass. We should only call it right
before we unwind the stack — not if we receive an immediate ping and
render the fiber again.
This was an oversight in 8ef3a7c that I didn't notice because it happens
to mostly work, anyway. What made me notice the mistake is that
throwException also marks the entire render phase as suspended
(RootDidSuspend or RootDidSuspendWithDelay), which is only supposed to
be happen if we show a fallback. One consequence was that, in the
RootDidSuspendWithDelay case, the entire commit phase was blocked,
because that's the exit status we use to block a bad fallback
from appearing.
* Use expando to check whether promise has resolved
Add a `status` expando to a thrown thenable to track when its value has
resolved.
In a later step, we'll also use `value` and `reason` expandos to track
the resolved value.
This is not part of the official JavaScript spec — think of
it as an extension of the Promise API, or a custom interface that is a
superset of Thenable. However, it's inspired by the terminology used
by `Promise.allSettled`.
The intent is that this will be a public API — Suspense implementations
can set these expandos to allow React to unwrap the value synchronously
without waiting a microtask.
* Scaffolding for `experimental_use` hook
Sets up a new experimental hook behind a feature flag, but does not
implement it yet.
* use(promise)
Adds experimental support to Fiber for unwrapping the value of a promise
inside a component. It is not yet implemented for Server Components,
but that is planned.
If promise has already resolved, the value can be unwrapped
"immediately" without showing a fallback. The trick we use to implement
this is to yield to the main thread (literally suspending the work
loop), wait for the microtask queue to drain, then check if the promise
resolved in the meantime. If so, we can resume the last attempted fiber
without unwinding the stack. This functionality was implemented in
previous commits.
Another feature is that the promises do not need to be cached between
attempts. Because we assume idempotent execution of components, React
will track the promises that were used during the previous attempt and
reuse the result. You shouldn't rely on this property, but during
initial render it mostly just works. Updates are trickier, though,
because if you used an uncached promise, we have no way of knowing
whether the underlying data has changed, so we have to unwrap the
promise every time. It will still work, but it's inefficient and can
lead to unnecessary fallbacks if it happens during a discrete update.
When we implement this for Server Components, this will be less of an
issue because there are no updates in that environment. However, it's
still better for performance to cache data requests, so the same
principles largely apply.
The intention is that this will eventually be the only supported way to
suspend on arbitrary promises. Throwing a promise directly will
be deprecated.
2022-08-26 02:12:07 +08:00
|
|
|
export const enableUseHook = false;
|
2022-08-23 16:36:02 +08:00
|
|
|
export const enableUseMemoCacheHook = false;
|
2022-09-15 02:39:06 +08:00
|
|
|
export const enableUseEventHook = false;
|
2022-04-20 05:34:30 +08:00
|
|
|
export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = true;
|
2022-06-16 23:38:37 +08:00
|
|
|
export const enableClientRenderFallbackOnTextMismatch = true;
|
2020-04-07 06:43:39 +08:00
|
|
|
export const enableComponentStackLocations = false;
|
2020-03-19 21:22:36 +08:00
|
|
|
export const enableLegacyFBSupport = false;
|
2020-07-31 22:01:27 +08:00
|
|
|
export const enableFilterEmptyStringAttributesDOM = false;
|
2020-12-02 23:25:55 +08:00
|
|
|
export const disableNativeComponentFrames = false;
|
2022-03-01 00:14:30 +08:00
|
|
|
export const skipUnmountedBoundaries = false;
|
2021-06-15 03:10:24 +08:00
|
|
|
export const deletedTreeCleanUpLevel = 3;
|
2021-07-26 21:56:59 +08:00
|
|
|
export const enableGetInspectorDataForInstanceInProduction = false;
|
2020-03-13 02:38:32 +08:00
|
|
|
export const enableNewReconciler = false;
|
2021-06-03 02:28:06 +08:00
|
|
|
export const deferRenderPhaseUpdateToNextBatch = false;
|
2020-03-13 02:38:32 +08:00
|
|
|
|
2021-02-25 05:14:14 +08:00
|
|
|
export const enableStrictEffects = false;
|
|
|
|
|
export const createRootStrictEffectsByDefault = false;
|
2020-10-20 04:05:00 +08:00
|
|
|
export const enableUseRefAccessWarning = false;
|
2020-09-25 04:42:17 +08:00
|
|
|
|
2020-12-18 06:17:23 +08:00
|
|
|
export const disableSchedulerTimeoutInWorkLoop = false;
|
2021-02-26 06:42:11 +08:00
|
|
|
export const enableLazyContextPropagation = false;
|
2022-03-10 00:48:03 +08:00
|
|
|
export const enableLegacyHidden = false;
|
2021-04-10 07:50:09 +08:00
|
|
|
export const enableSyncDefaultUpdates = true;
|
2021-04-29 04:09:30 +08:00
|
|
|
export const allowConcurrentByDefault = false;
|
2021-12-08 23:11:42 +08:00
|
|
|
export const enableCustomElementPropertySupport = false;
|
2020-10-28 03:02:19 +08:00
|
|
|
|
2021-09-02 02:56:52 +08:00
|
|
|
export const consoleManagedByDevToolsDuringStrictMode = false;
|
2022-03-08 20:55:32 +08:00
|
|
|
export const enableServerContext = false;
|
2021-11-01 12:39:51 +08:00
|
|
|
export const enableUseMutableSource = false;
|
2021-09-02 02:56:52 +08:00
|
|
|
|
2022-01-11 04:32:40 +08:00
|
|
|
export const enableTransitionTracing = false;
|
|
|
|
|
|
2022-08-13 04:27:53 +08:00
|
|
|
export const enableFloat = false;
|
2022-10-11 23:42:42 +08:00
|
|
|
export const enableHostSingletons = false;
|
2022-10-01 00:06:11 +08:00
|
|
|
|
|
|
|
|
export const useModernStrictMode = false;
|
2022-10-18 05:57:59 +08:00
|
|
|
export const enableFizzExternalRuntime = false;
|
2022-10-05 01:25:17 +08:00
|
|
|
|
2017-11-06 22:14:48 +08:00
|
|
|
// Flow magic to verify the exports of this file match the original version.
|
2022-10-05 01:25:17 +08:00
|
|
|
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
|