[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-18 18:28:23 -07:00
|
|
|
/**
|
2022-10-18 11:19:24 -04:00
|
|
|
* Copyright (c) Meta Platforms, Inc. and 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-18 18:28:23 -07: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 14:14:48 +00:00
|
|
|
import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
2020-02-13 20:33:53 +00: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-18 18:28:23 -07:00
|
|
|
|
2024-03-22 13:02:04 -04:00
|
|
|
// TODO: Align these flags with canary and delete this file once RN ships from Canary.
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
2024-12-18 17:51:12 -05:00
|
|
|
// All flags
|
2024-03-22 13:02:04 -04:00
|
|
|
// -----------------------------------------------------------------------------
|
2024-06-14 17:09:50 -07:00
|
|
|
export const alwaysThrottleRetries = false;
|
|
|
|
|
export const disableClientCache = true;
|
|
|
|
|
export const disableCommentsAsDOMContainers = true;
|
|
|
|
|
export const disableDefaultPropsExceptForClasses = true;
|
|
|
|
|
export const disableInputAttributeSyncing = false;
|
|
|
|
|
export const disableLegacyContext = true;
|
2024-07-11 16:21:12 -04:00
|
|
|
export const disableLegacyContextForFunctionComponents = true;
|
2024-06-14 17:09:50 -07:00
|
|
|
export const disableLegacyMode = false;
|
|
|
|
|
export const disableSchedulerTimeoutInWorkLoop = false;
|
|
|
|
|
export const disableTextareaChildren = false;
|
|
|
|
|
export const enableAsyncDebugInfo = false;
|
|
|
|
|
export const enableAsyncIterableChildren = false;
|
2024-03-22 13:02:04 -04:00
|
|
|
export const enableCPUSuspense = false;
|
2024-06-14 17:09:50 -07:00
|
|
|
export const enableCreateEventHandleAPI = false;
|
|
|
|
|
export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
|
2025-02-20 12:29:01 -05:00
|
|
|
export const enableFabricCompleteRootInCommitPhase = false;
|
2024-11-22 13:24:29 -05:00
|
|
|
export const enableMoveBefore = true;
|
2024-06-14 17:09:50 -07:00
|
|
|
export const enableFizzExternalRuntime = true;
|
2024-08-16 14:21:57 -07:00
|
|
|
export const enableHalt = false;
|
2024-09-13 13:18:14 -07:00
|
|
|
export const enableHiddenSubtreeInsertionEffectCleanup = false;
|
2024-10-01 11:00:57 -04:00
|
|
|
export const enableInfiniteRenderLoopDetection = false;
|
2022-10-23 23:20:52 -04:00
|
|
|
export const enableLegacyCache = false;
|
2024-06-14 17:09:50 -07:00
|
|
|
export const enableLegacyFBSupport = false;
|
|
|
|
|
export const enableLegacyHidden = false;
|
|
|
|
|
export const enableNoCloningMemoCache = false;
|
2024-07-10 16:42:08 -04:00
|
|
|
export const enableObjectFiber = false;
|
2025-02-18 10:29:40 -05:00
|
|
|
export const enableOwnerStacks = true;
|
2024-08-01 15:11:19 -04:00
|
|
|
export const enablePersistedModeClonedFlag = false;
|
2023-08-17 13:26:14 -04:00
|
|
|
export const enablePostpone = false;
|
2024-06-14 17:09:50 -07:00
|
|
|
export const enableReactTestRendererWarning = false;
|
|
|
|
|
export const enableRenderableContext = true;
|
|
|
|
|
export const enableRetryLaneExpiration = false;
|
|
|
|
|
export const enableSchedulingProfiler = __PROFILE__;
|
2024-09-16 11:09:40 -04:00
|
|
|
export const enableComponentPerformanceTrack = false;
|
2019-08-29 12:06:51 +01:00
|
|
|
export const enableScopeAPI = false;
|
2024-06-14 17:09:50 -07:00
|
|
|
export const enableShallowPropDiffing = false;
|
2021-09-20 15:44:48 -04:00
|
|
|
export const enableSuspenseAvoidThisFallback = false;
|
2024-06-14 17:09:50 -07:00
|
|
|
export const enableSuspenseCallback = false;
|
|
|
|
|
export const enableTaint = true;
|
|
|
|
|
export const enableTransitionTracing = false;
|
|
|
|
|
export const enableTrustedTypesIntegration = false;
|
2022-12-14 15:08:29 -05:00
|
|
|
export const enableUseEffectEventHook = false;
|
2024-03-26 19:52:46 -07:00
|
|
|
export const favorSafetyOverHydrationPerf = true;
|
2023-10-10 15:11:26 +01:00
|
|
|
export const passChildrenWhenCloningPersistedNodes = false;
|
2024-04-22 12:39:56 -04:00
|
|
|
export const renameElementSymbol = true;
|
2024-06-14 17:09:50 -07:00
|
|
|
export const retryLaneExpirationMs = 5000;
|
|
|
|
|
export const syncLaneExpirationMs = 250;
|
|
|
|
|
export const transitionLaneExpirationMs = 5000;
|
2024-11-14 11:48:14 -05:00
|
|
|
export const enableSiblingPrerendering = true;
|
2025-02-11 14:05:50 -05:00
|
|
|
export const enableUseEffectCRUDOverload = false;
|
2024-05-09 12:23:05 -04:00
|
|
|
|
2024-12-12 23:06:07 -05:00
|
|
|
export const enableHydrationLaneScheduling = true;
|
|
|
|
|
|
2024-12-17 17:01:31 -05:00
|
|
|
export const enableYieldingBeforePassive = false;
|
|
|
|
|
|
2025-01-02 13:02:22 -05:00
|
|
|
export const enableThrottledScheduling = false;
|
Add <ViewTransition> Component (#31975)
This will provide the opt-in for using [View
Transitions](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API)
in React.
View Transitions only trigger for async updates like `startTransition`,
`useDeferredValue`, Actions or `<Suspense>` revealing from fallback to
content. Synchronous updates provide an opt-out but also guarantee that
they commit immediately which View Transitions can't.
There's no need to opt-in to View Transitions at the "cause" side like
event handlers or actions. They don't know what UI will change and
whether that has an animated transition described.
Conceptually the `<ViewTransition>` component is like a DOM fragment
that transitions its children in its own isolate/snapshot. The API works
by wrapping a DOM node or inner component:
```js
import {ViewTransition} from 'react';
<ViewTransition><Component /></ViewTransition>
```
The default is `name="auto"` which will automatically assign a
`view-transition-name` to the inner DOM node. That way you can add a
View Transition to a Component without controlling its DOM nodes styling
otherwise.
A difference between this and the browser's built-in
`view-transition-name: auto` is that switching the DOM nodes within the
`<ViewTransition>` component preserves the same name so this example
cross-fades between the DOM nodes instead of causing an exit and enter:
```js
<ViewTransition>{condition ? <ComponentA /> : <ComponentB />}</ViewTransition>
```
This becomes especially useful with `<Suspense>` as this example
cross-fades between Skeleton and Content:
```js
<ViewTransition>
<Suspense fallback={<Skeleton />}>
<Content />
</Suspense>
</ViewTransition>
```
Where as this example triggers an exit of the Skeleton and an enter of
the Content:
```js
<Suspense fallback={<ViewTransition><Skeleton /></ViewTransition>}>
<ViewTransition><Content /></ViewTransition>
</Suspense>
```
Managing instances and keys becomes extra important.
You can also specify an explicit `name` property for example for
animating the same conceptual item from one page onto another. However,
best practices is to property namespace these since they can easily
collide. It's also useful to add an `id` to it if available.
```js
<ViewTransition name="my-shared-view">
```
The model in general is the same as plain `view-transition-name` except
React manages a set of heuristics for when to apply it. A problem with
the naive View Transitions model is that it overly opts in every
boundary that *might* transition into transitioning. This is leads to
unfortunate effects like things floating around when unrelated updates
happen. This leads the whole document to animate which means that
nothing is clickable in the meantime. It makes it not useful for smaller
and more local transitions. Best practice is to add
`view-transition-name` only right before you're about to need to animate
the thing. This is tricky to manage globally on complex apps and is not
compositional. Instead we let React manage when a `<ViewTransition>`
"activates" and add/remove the `view-transition-name`. This is also when
React calls `startViewTransition` behind the scenes while it mutates the
DOM.
I've come up with a number of heuristics that I think will make a lot
easier to coordinate this. The principle is that only if something that
updates that particular boundary do we activate it. I hope that one day
maybe browsers will have something like these built-in and we can remove
our implementation.
A `<ViewTransition>` only activates if:
- If a mounted Component renders a `<ViewTransition>` within it outside
the first DOM node, and it is within the viewport, then that
ViewTransition activates as an "enter" animation. This avoids inner
"enter" animations trigger when the parent mounts.
- If an unmounted Component had a `<ViewTransition>` within it outside
the first DOM node, and it was within the viewport, then that
ViewTransition activates as an "exit" animation. This avoids inner
"exit" animations triggering when the parent unmounts.
- If an explicitly named `<ViewTransition name="...">` is deep within an
unmounted tree and one with the same name appears in a mounted tree at
the same time, then both are activated as a pair, but only if they're
both in the viewport. This avoids these triggering "enter" or "exit"
animations when going between parents that don't have a pair.
- If an already mounted `<ViewTransition>` is visible and a DOM
mutation, that might affect how it's painted, happens within its
children but outside any nested `<ViewTransition>`. This allows it to
"cross-fade" between its updates.
- If an already mounted `<ViewTransition>` resizes or moves as the
result of direct DOM nodes siblings changing or moving around. This
allows insertion, deletion and reorders into a list to animate all
children. It is only within one DOM node though, to avoid unrelated
changes in the parent to trigger this. If an item is outside the
viewport before and after, then it's skipped to avoid things flying
across the screen.
- If a `<ViewTransition>` boundary changes size, due to a DOM mutation
within it, then the parent activates (or the root document if there are
no more parents). This ensures that the container can cross-fade to
avoid abrupt relayout. This can be avoided by using absolutely
positioned children. When this can avoid bubbling to the root document,
whatever is not animating is still responsive to clicks during the
transition.
Conceptually each DOM node has its own default that activates the parent
`<ViewTransition>` or no transition if the parent is the root. That
means that if you add a DOM node like `<div><ViewTransition><Component
/></ViewTransition></div>` this won't trigger an "enter" animation since
it was the div that was added, not the ViewTransition. Instead, it might
cause a cross-fade of the parent ViewTransition or no transition if it
had no parent. This ensures that only explicit boundaries perform coarse
animations instead of every single node which is really the benefit of
the View Transitions model. This ends up working out well for simple
cases like switching between two pages immediately while transitioning
one floating item that appears on both pages. Because only the floating
item transitions by default.
Note that it's possible to add manual `view-transition-name` with CSS or
`style={{ viewTransitionName: 'auto' }}` that always transitions as long
as something else has a `<ViewTransition>` that activates. For example a
`<ViewTransition>` can wrap a whole page for a cross-fade but inside of
it an explicit name can be added to something to ensure it animates as a
move when something relates else changes its layout. Instead of just
cross-fading it along with the Page which would be the default.
There's more PRs coming with some optimizations, fixes and expanded
APIs. This first PR explores the above core heuristic.
---------
Co-authored-by: Sebastian "Sebbie" Silbermann <silbermann.sebastian@gmail.com>
2025-01-08 12:11:18 -05:00
|
|
|
export const enableViewTransition = false;
|
2025-02-13 16:06:01 -05:00
|
|
|
export const enableSwipeTransition = false;
|
2025-01-30 11:16:42 +00:00
|
|
|
export const enableFastAddPropertiesInDiffing = false;
|
2025-02-12 13:52:57 +00:00
|
|
|
export const enableLazyPublicInstanceInFabric = false;
|
2025-01-02 13:02:22 -05:00
|
|
|
|
2024-03-22 13:02:04 -04:00
|
|
|
// Profiling Only
|
|
|
|
|
export const enableProfilerTimer = __PROFILE__;
|
|
|
|
|
export const enableProfilerCommitHooks = __PROFILE__;
|
|
|
|
|
export const enableProfilerNestedUpdatePhase = __PROFILE__;
|
|
|
|
|
export const enableUpdaterTracking = __PROFILE__;
|
2024-02-26 19:18:50 +01:00
|
|
|
|
2017-11-06 14:14:48 +00:00
|
|
|
// Flow magic to verify the exports of this file match the original version.
|
2022-10-04 13:25:17 -04:00
|
|
|
((((null: any): ExportsType): FeatureFlagsType): ExportsType);
|