react/packages/shared/forks/ReactFeatureFlags.testing.js
Andrew Clark d13f5b9538
Experiment: Unsuspend all lanes on update (#20660)
Adds a feature flag to tweak the internal heuristic used to "unsuspend"
lanes when a new update comes in.

A lane is "suspended" if we couldn't finish rendering it because it was
missing data, and we chose not to commit the fallback. (In this context,
"suspended" does not include updates that finished with a fallback.)

When we receive new data in the form of an update, we need to retry
rendering the suspended lanes, since the new data may have unblocked the
previously suspended work. For example, the new update could navigate
back to an already loaded route.

It's impractical to retry every combination of suspended lanes, so we
need some heuristic that decides which lanes to retry and in
which order.

The existing heuristic roughly approximates the old Expiration Times
model. It unsuspends all lower priority lanes, but leaves higher
priority lanes suspended.

Then when we start rendering, we choose the lanes that have the highest
LanePriority and render those -- and then we add to that all the lanes
that are highher priority.

If this sounds terribly confusing, it's because it barely makes sense.
(It made more sense in the Expiration Times world, I promise, but it
was still confusing.) I don't think it's worth me trying to explain the
old behavior too much because the point here is that we can replace it
with something simpler.

The new heurstic is to unsuspend all suspended lanes whenever there's
an update.

This is effectively what we already do except in a few very specific
edge cases, ever since we removed the delayed suspense feature from
everything that's not a refresh transition.

We can optimize this in the future to only unsuspend lanes that are
either 1) in the `lanes` or `subtreeLanes` of the node that was updated,
or 2) in the `lanes` of the return path of the node that was updated.
This would exclude lanes that are only located in unrelated sibling
trees. But, this optimization wouldn't be useful currently because we
assign the same transition lane to all transitions. It will become
relevant again once we start assigning arbitrary lanes to transitions
-- but that in turn requires us to implement entanglement of overlapping
transitions, one of our planned projects.

So to sum up: the goal here is to remove the weird edge cases and switch
to a simpler model, on top of which we can make more substantial
improvements.

I put it behind a flag so I can run an A/B test and confirm it doesn't
cause a regression.
2021-01-26 12:23:34 -08:00

67 lines
2.8 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.testing';
export const debugRenderPhaseSideEffectsForStrictMode = false;
export const enableDebugTracing = false;
export const enableSchedulingProfiler = false;
export const warnAboutDeprecatedLifecycles = true;
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
export const enableProfilerTimer = __PROFILE__;
export const enableProfilerCommitHooks = false;
export const enableProfilerNestedUpdatePhase = false;
export const enableProfilerNestedUpdateScheduledHook = false;
export const enableSchedulerTracing = __PROFILE__;
export const enableSuspenseServerRenderer = false;
export const enableSelectiveHydration = false;
export const enableLazyElements = false;
export const enableCache = false;
export const disableJavaScriptURLs = false;
export const disableInputAttributeSyncing = false;
export const enableSchedulerDebugging = false;
export const enableFundamentalAPI = false;
export const enableScopeAPI = false;
export const enableCreateEventHandleAPI = false;
export const warnAboutUnmockedScheduler = false;
export const enableSuspenseCallback = false;
export const warnAboutDefaultPropsOnFunctionComponents = false;
export const warnAboutStringRefs = false;
export const disableLegacyContext = false;
export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
export const enableTrustedTypesIntegration = false;
export const disableTextareaChildren = false;
export const disableModulePatternComponents = false;
export const warnUnstableRenderSubtreeIntoContainer = false;
export const warnAboutSpreadingKeyToJSX = false;
export const enableComponentStackLocations = true;
export const enableLegacyFBSupport = false;
export const enableFilterEmptyStringAttributesDOM = false;
export const disableNativeComponentFrames = false;
export const skipUnmountedBoundaries = false;
export const enableNewReconciler = false;
export const deferRenderPhaseUpdateToNextBatch = true;
export const decoupleUpdatePriorityFromScheduler = false;
export const enableDiscreteEventFlushingChange = false;
export const enableDoubleInvokingEffects = false;
export const enableUseRefAccessWarning = false;
export const enableRecursiveCommitTraversal = false;
export const disableSchedulerTimeoutInWorkLoop = false;
export const enableTransitionEntanglement = false;
// Flow magic to verify the exports of this file match the original version.
// eslint-disable-next-line no-unused-vars
type Check<_X, Y: _X, X: Y = _X> = null;
// eslint-disable-next-line no-unused-expressions
(null: Check<ExportsType, FeatureFlagsType>);