Add `alwaysThrottleRetries` flag (#26685)

This puts the change introduced by #26611 behind a flag until Meta is
able to roll it out. Disabling the flag reverts back to the old
behavior, where retries are throttled if there's still data remaining in
the tree, but not if all the data has finished loading.

The new behavior is still enabled in the public builds.
This commit is contained in:
Andrew Clark 2023-04-20 14:23:22 -04:00 committed by GitHub
parent 7f8c501f68
commit d73d7d5908
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 45 additions and 6 deletions

View File

@ -39,6 +39,7 @@ import {
enableTransitionTracing,
useModernStrictMode,
disableLegacyContext,
alwaysThrottleRetries,
} from 'shared/ReactFeatureFlags';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import is from 'shared/objectIs';
@ -1115,7 +1116,10 @@ function finishConcurrentRender(
workInProgressTransitions,
);
} else {
if (includesOnlyRetries(lanes)) {
if (
includesOnlyRetries(lanes) &&
(alwaysThrottleRetries || exitStatus === RootSuspended)
) {
// This render only included retries, no updates. Throttle committing
// retries so that we don't show too many loading states too quickly.
const msUntilTimeout =

View File

@ -1779,10 +1779,28 @@ describe('ReactSuspenseWithNoopRenderer', () => {
await resolveText('B');
expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
// Restart and render the complete content. The tree will finish but we
// won't commit the result yet because the fallback appeared recently.
// Restart and render the complete content.
await waitForAll(['A', 'B']);
expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
if (gate(flags => flags.alwaysThrottleRetries)) {
// Correct behavior:
//
// The tree will finish but we won't commit the result yet because the fallback appeared recently.
expect(ReactNoop).toMatchRenderedOutput(<span prop="Loading..." />);
} else {
// Old behavior, gated until this rolls out at Meta:
//
// TODO: Because this render was the result of a retry, and a fallback
// was shown recently, we should suspend and remain on the fallback for
// little bit longer. We currently only do this if there's still
// remaining fallbacks in the tree, but we should do it for all retries.
expect(ReactNoop).toMatchRenderedOutput(
<>
<span prop="A" />
<span prop="B" />
</>,
);
}
});
assertLog([]);
expect(ReactNoop).toMatchRenderedOutput(

View File

@ -124,6 +124,8 @@ export const diffInCommitPhase = __EXPERIMENTAL__;
export const enableAsyncActions = __EXPERIMENTAL__;
export const alwaysThrottleRetries = true;
// -----------------------------------------------------------------------------
// Chopping Block
//

View File

@ -22,6 +22,7 @@ import typeof * as DynamicFlagsType from 'ReactNativeInternalFeatureFlags';
export const enableUseRefAccessWarning = __VARIANT__;
export const enableDeferRootSchedulingToMicrotask = __VARIANT__;
export const alwaysThrottleRetries = __VARIANT__;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): DynamicFlagsType): ExportsType);

View File

@ -17,8 +17,11 @@ import * as dynamicFlags from 'ReactNativeInternalFeatureFlags';
// We destructure each value before re-exporting to avoid a dynamic look-up on
// the exports object every time a flag is read.
export const {enableUseRefAccessWarning, enableDeferRootSchedulingToMicrotask} =
dynamicFlags;
export const {
enableUseRefAccessWarning,
enableDeferRootSchedulingToMicrotask,
alwaysThrottleRetries,
} = dynamicFlags;
// The rest of the flags are static for better dead code elimination.
export const enableDebugTracing = false;

View File

@ -75,5 +75,7 @@ export const enableDeferRootSchedulingToMicrotask = true;
export const diffInCommitPhase = true;
export const enableAsyncActions = false;
export const alwaysThrottleRetries = true;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

View File

@ -75,5 +75,7 @@ export const enableDeferRootSchedulingToMicrotask = true;
export const diffInCommitPhase = true;
export const enableAsyncActions = false;
export const alwaysThrottleRetries = true;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

View File

@ -72,5 +72,7 @@ export const enableDeferRootSchedulingToMicrotask = true;
export const diffInCommitPhase = true;
export const enableAsyncActions = false;
export const alwaysThrottleRetries = true;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

View File

@ -77,5 +77,7 @@ export const enableDeferRootSchedulingToMicrotask = true;
export const diffInCommitPhase = true;
export const enableAsyncActions = false;
export const alwaysThrottleRetries = true;
// Flow magic to verify the exports of this file match the original version.
((((null: any): ExportsType): FeatureFlagsType): ExportsType);

View File

@ -27,6 +27,7 @@ export const enableCustomElementPropertySupport = __VARIANT__;
export const enableDeferRootSchedulingToMicrotask = __VARIANT__;
export const diffInCommitPhase = __VARIANT__;
export const enableAsyncActions = __VARIANT__;
export const alwaysThrottleRetries = __VARIANT__;
// Enable this flag to help with concurrent mode debugging.
// It logs information to the console about React scheduling, rendering, and commit phases.

View File

@ -30,6 +30,7 @@ export const {
enableDeferRootSchedulingToMicrotask,
diffInCommitPhase,
enableAsyncActions,
alwaysThrottleRetries,
} = dynamicFeatureFlags;
// On WWW, __EXPERIMENTAL__ is used for a new modern build.

View File

@ -10,4 +10,5 @@
declare module 'ReactNativeInternalFeatureFlags' {
declare export var enableUseRefAccessWarning: boolean;
declare export var enableDeferRootSchedulingToMicrotask: boolean;
declare export var alwaysThrottleRetries: boolean;
}