Skip to content

Commit 9550dbd

Browse files
committed
Add a feature flag
1 parent ef8c6e6 commit 9550dbd

12 files changed

+55
-18
lines changed

packages/react-reconciler/src/ReactFiberHooks.new.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import type {HookFlags} from './ReactHookEffectTags';
2222
import type {FiberRoot} from './ReactInternalTypes';
2323
import type {Cache} from './ReactFiberCacheComponent.new';
2424
import type {Flags} from './ReactFiberFlags';
25+
import {enableThrowOnMountForHookMismatch} from 'shared/ReactFeatureFlags';
2526

2627
import ReactSharedInternals from 'shared/ReactSharedInternals';
2728
import {
@@ -290,7 +291,7 @@ function warnOnHookMismatchInDev(currentHookName: ?HookType) {
290291
? null
291292
: i === ((hookTypesUpdateIndexDev: any): number)
292293
? currentHookName
293-
: oldHookName) ?? undefined;
294+
: oldHookName) ?? 'undefined';
294295

295296
let row = `${i + 1}. ${oldHookName}`;
296297

@@ -423,7 +424,9 @@ export function renderWithHooks<Props, SecondArg>(
423424
if (__DEV__) {
424425
if (
425426
current !== null &&
426-
(current.mode & ConcurrentMode || current.memoizedState !== null)
427+
((enableThrowOnMountForHookMismatch &&
428+
(current.mode & ConcurrentMode) !== NoMode) ||
429+
current.memoizedState !== null)
427430
) {
428431
ReactCurrentDispatcher.current = HooksDispatcherOnUpdateInDEV;
429432
} else if (hookTypesDev !== null) {
@@ -438,11 +441,12 @@ export function renderWithHooks<Props, SecondArg>(
438441
}
439442
} else {
440443
ReactCurrentDispatcher.current =
441-
current === null ||
442-
((current.mode & ConcurrentMode) === NoMode &&
443-
current.memoizedState === null)
444-
? HooksDispatcherOnMount
445-
: HooksDispatcherOnUpdate;
444+
current !== null &&
445+
((enableThrowOnMountForHookMismatch &&
446+
(current.mode & ConcurrentMode) !== NoMode) ||
447+
current.memoizedState !== null)
448+
? HooksDispatcherOnUpdate
449+
: HooksDispatcherOnMount;
446450
}
447451

448452
let children = Component(props, secondArg);

packages/react-reconciler/src/ReactFiberHooks.old.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import type {HookFlags} from './ReactHookEffectTags';
2222
import type {FiberRoot} from './ReactInternalTypes';
2323
import type {Cache} from './ReactFiberCacheComponent.old';
2424
import type {Flags} from './ReactFiberFlags';
25+
import {enableThrowOnMountForHookMismatch} from 'shared/ReactFeatureFlags';
2526

2627
import ReactSharedInternals from 'shared/ReactSharedInternals';
2728
import {
@@ -290,7 +291,7 @@ function warnOnHookMismatchInDev(currentHookName: ?HookType) {
290291
? null
291292
: i === ((hookTypesUpdateIndexDev: any): number)
292293
? currentHookName
293-
: oldHookName) ?? undefined;
294+
: oldHookName) ?? 'undefined';
294295

295296
let row = `${i + 1}. ${oldHookName}`;
296297

@@ -423,7 +424,9 @@ export function renderWithHooks<Props, SecondArg>(
423424
if (__DEV__) {
424425
if (
425426
current !== null &&
426-
(current.mode & ConcurrentMode || current.memoizedState !== null)
427+
((enableThrowOnMountForHookMismatch &&
428+
(current.mode & ConcurrentMode) !== NoMode) ||
429+
current.memoizedState !== null)
427430
) {
428431
ReactCurrentDispatcher.current = HooksDispatcherOnUpdateInDEV;
429432
} else if (hookTypesDev !== null) {
@@ -438,11 +441,12 @@ export function renderWithHooks<Props, SecondArg>(
438441
}
439442
} else {
440443
ReactCurrentDispatcher.current =
441-
current === null ||
442-
((current.mode & ConcurrentMode) === NoMode &&
443-
current.memoizedState === null)
444-
? HooksDispatcherOnMount
445-
: HooksDispatcherOnUpdate;
444+
current !== null &&
445+
((enableThrowOnMountForHookMismatch &&
446+
(current.mode & ConcurrentMode) !== NoMode) ||
447+
current.memoizedState !== null)
448+
? HooksDispatcherOnUpdate
449+
: HooksDispatcherOnMount;
446450
}
447451

448452
let children = Component(props, secondArg);

packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3866,9 +3866,14 @@ describe('ReactHooksWithNoopRenderer', () => {
38663866

38673867
ReactNoop.render(<App loadA={true} />);
38683868
expect(() => {
3869-
expect(() => {
3869+
const expectFlush = expect(() => {
38703870
expect(Scheduler).toFlushAndYield(['A: 0']);
3871-
}).toThrow('Rendered more hooks than during the previous render');
3871+
});
3872+
if (gate(flag => flag.enableThrowOnMountForHookMismatch)) {
3873+
expectFlush.toThrow(
3874+
'Rendered more hooks than during the previous render',
3875+
);
3876+
}
38723877
}).toErrorDev([
38733878
'Warning: React has detected a change in the order of Hooks called by App. ' +
38743879
'This will lead to bugs and errors if not fixed. For more information, ' +
@@ -3970,9 +3975,14 @@ describe('ReactHooksWithNoopRenderer', () => {
39703975
act(() => {
39713976
ReactNoop.render(<App showMore={true} />);
39723977
expect(() => {
3973-
expect(() => {
3978+
const expectFlush = expect(() => {
39743979
expect(Scheduler).toFlushAndYield(['Mount A']);
3975-
}).toThrow('Rendered more hooks than during the previous render');
3980+
});
3981+
if (gate(flags => flags.enableThrowOnMountForHookMismatch)) {
3982+
expectFlush.toThrow(
3983+
'Rendered more hooks than during the previous render',
3984+
);
3985+
}
39763986
}).toErrorDev([
39773987
'Warning: React has detected a change in the order of Hooks called by App. ' +
39783988
'This will lead to bugs and errors if not fixed. For more information, ' +

packages/shared/ReactFeatureFlags.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,7 @@ export const enableGetInspectorDataForInstanceInProduction = false;
258258
export const enableProfilerNestedUpdateScheduledHook = false;
259259

260260
export const consoleManagedByDevToolsDuringStrictMode = true;
261+
262+
// When going between 0 to n hooks in concurrent mode, throw "Rendered more/fewer" hooks than expected.
263+
// This may be an invasive change so we're rolling it out slower.
264+
export const enableThrowOnMountForHookMismatch = false;

packages/shared/forks/ReactFeatureFlags.native-fb.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ export const enableUseMutableSource = true;
8383
export const enableTransitionTracing = false;
8484

8585
export const enableFloat = false;
86+
87+
export const enableThrowOnMountForHookMismatch = false;
8688
// Flow magic to verify the exports of this file match the original version.
8789
// eslint-disable-next-line no-unused-vars
8890
type Check<_X, Y: _X, X: Y = _X> = null;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ export const enableUseMutableSource = false;
7272
export const enableTransitionTracing = false;
7373

7474
export const enableFloat = false;
75+
76+
export const enableThrowOnMountForHookMismatch = false;
7577
// Flow magic to verify the exports of this file match the original version.
7678
// eslint-disable-next-line no-unused-vars
7779
type Check<_X, Y: _X, X: Y = _X> = null;

packages/shared/forks/ReactFeatureFlags.test-renderer.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ export const enableUseMutableSource = false;
7272
export const enableTransitionTracing = false;
7373

7474
export const enableFloat = false;
75+
76+
export const enableThrowOnMountForHookMismatch = false;
7577
// Flow magic to verify the exports of this file match the original version.
7678
// eslint-disable-next-line no-unused-vars
7779
type Check<_X, Y: _X, X: Y = _X> = null;

packages/shared/forks/ReactFeatureFlags.test-renderer.www.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ export const enableUseMutableSource = true;
7474
export const enableTransitionTracing = false;
7575

7676
export const enableFloat = false;
77+
78+
export const enableThrowOnMountForHookMismatch = false;
7779
// Flow magic to verify the exports of this file match the original version.
7880
// eslint-disable-next-line no-unused-vars
7981
type Check<_X, Y: _X, X: Y = _X> = null;

packages/shared/forks/ReactFeatureFlags.testing.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ export const enableUseMutableSource = false;
7272
export const enableTransitionTracing = false;
7373

7474
export const enableFloat = false;
75+
76+
export const enableThrowOnMountForHookMismatch = false;
7577
// Flow magic to verify the exports of this file match the original version.
7678
// eslint-disable-next-line no-unused-vars
7779
type Check<_X, Y: _X, X: Y = _X> = null;

packages/shared/forks/ReactFeatureFlags.testing.www.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ export const enableUseMutableSource = true;
7373
export const enableTransitionTracing = false;
7474

7575
export const enableFloat = false;
76+
77+
export const enableThrowOnMountForHookMismatch = false;
78+
7679
// Flow magic to verify the exports of this file match the original version.
7780
// eslint-disable-next-line no-unused-vars
7881
type Check<_X, Y: _X, X: Y = _X> = null;

packages/shared/forks/ReactFeatureFlags.www-dynamic.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export const consoleManagedByDevToolsDuringStrictMode = __VARIANT__;
2828
export const enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay = __VARIANT__;
2929
export const enableClientRenderFallbackOnTextMismatch = __VARIANT__;
3030
export const enableTransitionTracing = __VARIANT__;
31+
export const enableThrowOnMountForHookMismatch = __VARIANT__;
3132
// Enable this flag to help with concurrent mode debugging.
3233
// It logs information to the console about React scheduling, rendering, and commit phases.
3334
//

packages/shared/forks/ReactFeatureFlags.www.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const {
3434
enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay,
3535
enableClientRenderFallbackOnTextMismatch,
3636
enableTransitionTracing,
37+
enableThrowOnMountForHookMismatch,
3738
} = dynamicFeatureFlags;
3839

3940
// On WWW, __EXPERIMENTAL__ is used for a new modern build.

0 commit comments

Comments
 (0)