Files
react/packages/shared/forks/ReactFeatureFlags.native-oss.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

87 lines
3.6 KiB
JavaScript
Raw Normal View History

[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
/**
* 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
*/
import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
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
// TODO: Align these flags with canary and delete this file once RN ships from Canary.
// -----------------------------------------------------------------------------
// All flags
// -----------------------------------------------------------------------------
export const alwaysThrottleRetries = false;
export const disableClientCache = true;
export const disableCommentsAsDOMContainers = true;
export const disableDefaultPropsExceptForClasses = true;
export const disableInputAttributeSyncing = false;
export const disableLegacyContext = true;
export const disableLegacyContextForFunctionComponents = true;
export const disableLegacyMode = false;
export const disableSchedulerTimeoutInWorkLoop = false;
export const disableTextareaChildren = false;
export const enableAsyncDebugInfo = false;
export const enableAsyncIterableChildren = false;
export const enableCPUSuspense = false;
export const enableCreateEventHandleAPI = false;
export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
export const enableFabricCompleteRootInCommitPhase = false;
Add moveBefore Experiment (#31596) A long standing issue for React has been that if you reorder stateful nodes, they may lose their state and reload. The thing moving loses its state. There's no way to solve this in general where two stateful nodes swap. The [`moveBefore()` proposal](https://chromestatus.com/feature/5135990159835136?gate=5177450351558656) has now moved to [intent-to-ship](https://groups.google.com/a/chromium.org/g/blink-dev/c/YE_xLH6MkRs/m/_7CD0NYMAAAJ). This function is kind of like `insertBefore` but preserves state. There's [a demo here](https://state-preserving-atomic-move.glitch.me/). Ideally we'd port this demo to a fixture so we can try it. Currently this flag is always off - even in experimental. That's because this is still behind a Chrome flag so it's a little early to turn it on even in experimental. So you need a custom build. It's on in RN but only because it doesn't apply there which makes it easier to tell that it's safe to ship once it's on everywhere else. The other reason it's still off is because there's currently a semantic breaking change. `moveBefore()` errors if both nodes are disconnected. That happens if we're inside a completely disconnected React root. That's not usually how you should use React because it means effects can't read layout etc. However, it is currently supported. To handle this we'd have to try/catch the `moveBefore` to handle this case but we hope this semantic will change before it ships. Before we turn this on in experimental we either have to wait for the implementation to not error in the disconnected-disconnected case in Chrome or we'd have to add try/catch.
2024-11-22 13:24:29 -05:00
export const enableMoveBefore = true;
export const enableFizzExternalRuntime = true;
export const enableHalt = false;
export const enableHiddenSubtreeInsertionEffectCleanup = false;
export const enableInfiniteRenderLoopDetection = false;
export const enableLegacyCache = false;
export const enableLegacyFBSupport = false;
export const enableLegacyHidden = false;
export const enableNoCloningMemoCache = false;
export const enableObjectFiber = false;
export const enableOwnerStacks = true;
export const enablePersistedModeClonedFlag = false;
Add Postpone API (#27238) This adds an experimental `unstable_postpone(reason)` API. Currently we don't have a way to model effectively an Infinite Promise. I.e. something that suspends but never resolves. The reason this is useful is because you might have something else that unblocks it later. E.g. by updating in place later, or by client rendering. On the client this works to model as an Infinite Promise (in fact, that's what this implementation does). However, in Fizz and Flight that doesn't work because the stream needs to end at some point. We don't have any way of knowing that we're suspended on infinite promises. It's not enough to tag the promises because you could await those and thus creating new promises. The only way we really have to signal this through a series of indirections like async functions, is by throwing. It's not 100% safe because these values can be caught but it's the best we can do. Effectively `postpone(reason)` behaves like a built-in [Catch Boundary](https://github.com/facebook/react/pull/26854). It's like `raise(Postpone, reason)` except it's built-in so it needs to be able to be encoded and caught by Suspense boundaries. In Flight and Fizz these behave pretty much the same as errors. Flight just forwards it to retrigger on the client. In Fizz they just trigger client rendering which itself might just postpone again or fill in the value. The difference is how they get logged. In Flight and Fizz they log to `onPostpone(reason)` instead of `onError(error)`. This log is meant to help find deopts on the server like finding places where you fall back to client rendering. The reason that you pass in is for that purpose to help the reason for any deopts. I do track the stack trace in DEV but I don't currently expose it to `onPostpone`. This seems like a limitation. It might be better to expose the Postpone object which is an Error object but that's more of an implementation detail. I could also pass it as a second argument. On the client after hydration they don't get passed to `onRecoverableError`. There's no global `onPostpone` API to capture postponed things on the client just like there's no `onError`. At that point it's just assumed to be intentional. It doesn't have any `digest` or reason passed to the client since it's not logged. There are some hacky solutions that currently just tries to reuse as much of the existing code as possible but should be more properly implemented. - Fiber is currently just converting it to a fake Promise object so that it behaves like an infinite Promise. - Fizz is encoding the magic digest string `"POSTPONE"` in the HTML so we know to ignore it but it should probably just be something neater that doesn't share namespace with digests. Next I plan on using this in the `/static` entry points for additional features. Why "postpone"? It's basically a synonym to "defer" but we plan on using "defer" for other purposes and it's overloaded anyway.
2023-08-17 13:26:14 -04:00
export const enablePostpone = false;
export const enableReactTestRendererWarning = false;
export const enableRenderableContext = true;
export const enableRetryLaneExpiration = false;
export const enableSchedulingProfiler = __PROFILE__;
export const enableComponentPerformanceTrack = false;
export const enableScopeAPI = false;
export const enableShallowPropDiffing = false;
export const enableSuspenseAvoidThisFallback = false;
export const enableSuspenseCallback = false;
export const enableTaint = true;
export const enableTransitionTracing = false;
export const enableTrustedTypesIntegration = false;
export const enableUseEffectEventHook = false;
Add Flag to Favor Hydration Performance over User Safety (#28655) If false, this ignores text comparison checks during hydration at the risk of privacy safety. Since React 18 we recreate the DOM starting from the nearest Suspense boundary if any of the text content mismatches. This ensures that if we have nodes that otherwise line up correctly such as if they're the same type of Component but in a different order, then we don't accidentally transfer state or attributes to the wrong one. If we didn't do this e.g. attributes like image src might not line up with the text. E.g. you might show the wrong profile picture with the wrong name. However, the main reason we do this is because it's a security/privacy concern if state from the original node can transfer to the other one. For example if you start typing into a text field to reply to a story but then it turns out that the hydration was in a different order, you might submit that text into a different story than you intended. Similarly, if you've already clicked an item and that gets replayed using Action replaying or is synchronously force hydrated - that click might end up applying to a different item in the list than you intended. E.g. liking the wrong photo. Unfortunately a common case where this happens is when Google Translate is applied to a page. It'll always cause mismatches and recreate the tree. Most of the time this wouldn't be visible to users because it'd just recreate to the same thing and then translate again. It can affect metrics that trace when this hydration happened though. Meta can use this flag to decide if they favor this perf metric over the risk to user privacy. This is similar to the old enableClientRenderFallbackOnTextMismatch flag except this flag doesn't patch up the text when there's a mismatch. Because we don't have the patching anymore. The assumption is that it is safe to ignore the safety concern because we assume it's a match and therefore favoring not patching it will lead to better perf.
2024-03-26 19:52:46 -07:00
export const favorSafetyOverHydrationPerf = true;
export const passChildrenWhenCloningPersistedNodes = false;
export const renameElementSymbol = true;
export const retryLaneExpirationMs = 5000;
export const syncLaneExpirationMs = 250;
export const transitionLaneExpirationMs = 5000;
export const enableSiblingPrerendering = true;
export const enableUseEffectCRUDOverload = false;
Track Stack of JSX Calls (#29032) This is the first step to experimenting with a new type of stack traces behind the `enableOwnerStacks` flag - in DEV only. The idea is to generate stacks that are more like if the JSX was a direct call even though it's actually a lazy call. Not only can you see which exact JSX call line number generated the erroring component but if that's inside an abstraction function, which function called that function and if it's a component, which component generated that component. For this to make sense it really need to be the "owner" stack rather than the parent stack like we do for other component stacks. On one hand it has more precise information but on the other hand it also loses context. For most types of problems the owner stack is the most useful though since it tells you which component rendered this component. The problem with the platform in its current state is that there's two ways to deal with stacks: 1) `new Error().stack` 2) `console.createTask()` The nice thing about `new Error().stack` is that we can extract the frames and piece them together in whatever way we want. That is great for constructing custom UIs like error dialogs. Unfortunately, we can't take custom stacks and set them in the native UIs like Chrome DevTools. The nice thing about `console.createTask()` is that the resulting stacks are natively integrated into the Chrome DevTools in the console and the breakpoint debugger. They also automatically follow source mapping and ignoreLists. The downside is that there's no way to extract the async stack outside the native UI itself so this information cannot be used for custom UIs like errors dialogs. It also means we can't collect this on the server and then pass it to the client for server components. The solution here is that we use both techniques and collect both an `Error` object and a `Task` object for every JSX call. The main concern about this approach is the performance so that's the main thing to test. It's certainly too slow for production but it might also be too slow even for DEV. This first PR doesn't actually use the stacks yet. It just collects them as the first step. The next step is to start utilizing this information in error printing etc. For RSC we pass the stack along across over the wire. This can be concatenated on the client following the owner path to create an owner stack leading back into the server. We'll later use this information to restore fake frames on the client for native integration. Since this information quickly gets pretty heavy if we include all frames, we strip out the top frame. We also strip out everything below the functions that call into user space in the Flight runtime. To do this we need to figure out the frames that represents calling out into user space. The resulting stack is typically just the one frame inside the owner component's JSX callsite. I also eagerly strip out things we expect to be ignoreList:ed anyway - such as `node_modules` and Node.js internals.
2024-05-09 12:23:05 -04:00
export const enableHydrationLaneScheduling = true;
export const enableYieldingBeforePassive = false;
[Fiber] Yield every other frame for Transition/Retry work (#31828) This flag first moves the `shouldYield()` logic into React itself. We need this for `postTask` compatibility anyway since this logic is no longer a concern of the scheduler. This means that there can also be no global `requestPaint()` that asks for painting earlier. So this is best rolled out with `enableAlwaysYieldScheduler` (and ideally `enableYieldingBeforePassive`) instead of `enableRequestPaint`. Once in React we can change the yield timing heuristics. This uses the previous 5ms for Idle work to keep everything responsive while doing background work. However, for Transitions and Retries we have seen that same thread animations (like loading states animating, or constant animations like cool Three.js stuff) can take CPU time away from the Transition that causes moving into new content to slow down. Therefore we only yield every 25ms. The purpose of this yield is not to avoid the overhead of yielding, which is very low, but rather to intentionally block any frequently occurring other main thread work like animations from starving our work. If we could we could just tell everyone else to throttle their stuff for ideal scheduling but that's not quite realistic. In other words, the purpose of this is to reduce the frame rate of animations to 30 fps and we achieve this by not yielding. We still do yield to allow the animations to not just stall. This seems like a good balance. The 5ms of Idle is because we don't really need to yield less often since the overhead is low. We keep it low to allow 120 fps animations to run if necessary and our work may not be the only work within a frame so we need to yield early enough to leave enough time left. Similarly we choose 25ms rather than say 35ms to ensure that we push long enough to guarantee to half the frame rate but low enough that there's plenty of time left for a rAF to power each animation every other frame. It's also low enough that if something else interrupts the work like a new interaction, we can still be responsive to that within 50ms or so. We also need to yield in case there's I/O work that needs to get bounced through the main thread. This flag is currently off everywhere since we have so many other scheduling flags but that means there's some urgency to roll those out fully so we can test this one. There's also some tests to update since this doesn't go through the Mock scheduler anymore for yields.
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;
Add useSwipeTransition Hook Behind Experimental Flag (#32373) This Hook will be used to drive a View Transition based on a gesture. ```js const [value, startGesture] = useSwipeTransition(prev, current, next); ``` The `enableSwipeTransition` flag will depend on `enableViewTransition` flag but we may decide to ship them independently. This PR doesn't do anything interesting yet. There will be a lot more PRs to build out the actual functionality. This is just wiring up the plumbing for the new Hook. This first PR is mainly concerned with how the whole starts (and stops). The core API is the `startGesture` function (although there will be other conveniences added in the future). You can call this to start a gesture with a source provider. You can call this multiple times in one event to batch multiple Hooks listening to the same provider. However, each render can only handle one source provider at a time and so it does one render per scheduled gesture provider. This uses a separate `GestureLane` to drive gesture renders by marking the Hook as having an update on that lane. Then schedule a render. These renders should be blocking and in the same microtask as the `startGesture` to ensure it can block the paint. So it's similar to sync. It may not be possible to finish it synchronously e.g. if something suspends. If so, it just tries again later when it can like any other render. This can also happen because it also may not be possible to drive more than one gesture at a time like if we're limited to one View Transition per document. So right now you can only run one gesture at a time in practice. These renders never commit. This means that we can't clear the `GestureLane` the normal way. Instead, we have to clear only the root's `pendingLanes` if we don't have any new renders scheduled. Then wait until something else updates the Fiber after all gestures on it have stopped before it really clears.
2025-02-13 16:06:01 -05:00
export const enableSwipeTransition = false;
export const enableFastAddPropertiesInDiffing = false;
export const enableLazyPublicInstanceInFabric = false;
[Fiber] Yield every other frame for Transition/Retry work (#31828) This flag first moves the `shouldYield()` logic into React itself. We need this for `postTask` compatibility anyway since this logic is no longer a concern of the scheduler. This means that there can also be no global `requestPaint()` that asks for painting earlier. So this is best rolled out with `enableAlwaysYieldScheduler` (and ideally `enableYieldingBeforePassive`) instead of `enableRequestPaint`. Once in React we can change the yield timing heuristics. This uses the previous 5ms for Idle work to keep everything responsive while doing background work. However, for Transitions and Retries we have seen that same thread animations (like loading states animating, or constant animations like cool Three.js stuff) can take CPU time away from the Transition that causes moving into new content to slow down. Therefore we only yield every 25ms. The purpose of this yield is not to avoid the overhead of yielding, which is very low, but rather to intentionally block any frequently occurring other main thread work like animations from starving our work. If we could we could just tell everyone else to throttle their stuff for ideal scheduling but that's not quite realistic. In other words, the purpose of this is to reduce the frame rate of animations to 30 fps and we achieve this by not yielding. We still do yield to allow the animations to not just stall. This seems like a good balance. The 5ms of Idle is because we don't really need to yield less often since the overhead is low. We keep it low to allow 120 fps animations to run if necessary and our work may not be the only work within a frame so we need to yield early enough to leave enough time left. Similarly we choose 25ms rather than say 35ms to ensure that we push long enough to guarantee to half the frame rate but low enough that there's plenty of time left for a rAF to power each animation every other frame. It's also low enough that if something else interrupts the work like a new interaction, we can still be responsive to that within 50ms or so. We also need to yield in case there's I/O work that needs to get bounced through the main thread. This flag is currently off everywhere since we have so many other scheduling flags but that means there's some urgency to roll those out fully so we can test this one. There's also some tests to update since this doesn't go through the Mock scheduler anymore for yields.
2025-01-02 13:02:22 -05:00
// Profiling Only
export const enableProfilerTimer = __PROFILE__;
export const enableProfilerCommitHooks = __PROFILE__;
export const enableProfilerNestedUpdatePhase = __PROFILE__;
export const enableUpdaterTracking = __PROFILE__;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);