diff --git a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js index 8c5eeb00459..555c54148ec 100644 --- a/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js +++ b/packages/eslint-plugin-react-hooks/__tests__/ESLintRuleExhaustiveDeps-test.js @@ -8344,6 +8344,23 @@ const testsTypescript = { }, ], }, + { + code: normalizeIndent` + function MyComponent(props) { + useEffect(() => { + console.log(props.foo); + }); + } + `, + options: [{requireExplicitEffectDeps: true}], + errors: [ + { + message: + 'React Hook useEffect always requires dependencies. Please add a dependency array or an explicit `undefined`', + suggestions: undefined, + }, + ], + }, ], }; diff --git a/packages/eslint-plugin-react-hooks/src/rules/ExhaustiveDeps.ts b/packages/eslint-plugin-react-hooks/src/rules/ExhaustiveDeps.ts index 624d28e3b33..d59a1ff7920 100644 --- a/packages/eslint-plugin-react-hooks/src/rules/ExhaustiveDeps.ts +++ b/packages/eslint-plugin-react-hooks/src/rules/ExhaustiveDeps.ts @@ -67,6 +67,9 @@ const rule = { type: 'string', }, }, + requireExplicitEffectDeps: { + type: 'boolean', + } }, }, ], @@ -90,10 +93,13 @@ const rule = { ? rawOptions.experimental_autoDependenciesHooks : []; + const requireExplicitEffectDeps: boolean = rawOptions && rawOptions.requireExplicitEffectDeps || false; + const options = { additionalHooks, experimental_autoDependenciesHooks, enableDangerousAutofixThisMayCauseInfiniteLoops, + requireExplicitEffectDeps, }; function reportProblem(problem: Rule.ReportDescriptor) { @@ -1340,6 +1346,15 @@ const rule = { return; } + if (!maybeNode && isEffect && options.requireExplicitEffectDeps) { + reportProblem({ + node: reactiveHook, + message: + `React Hook ${reactiveHookName} always requires dependencies. ` + + `Please add a dependency array or an explicit \`undefined\`` + }); + } + const isAutoDepsHook = options.experimental_autoDependenciesHooks.includes(reactiveHookName); diff --git a/packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetInlineCodeStrings.js b/packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetInlineCodeStrings.js index 363e11f988d..e74e4bbe86b 100644 --- a/packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetInlineCodeStrings.js +++ b/packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetInlineCodeStrings.js @@ -8,7 +8,7 @@ export const clientRenderBoundary = export const completeBoundary = '$RB=[];$RV=function(c){$RT=performance.now();for(var a=0;a { + revealBoundaries( + batch, + // Force layout to trigger font loading, we pass the actual value to trick minifiers. + document.documentElement.clientHeight, + ); + return Promise.race([ + // Block on fonts finishing loading before revealing these boundaries. + document.fonts.ready, + new Promise(resolve => setTimeout(resolve, SUSPENSEY_FONT_TIMEOUT)), + ]); + }, types: [], // TODO: Add a hard coded type for Suspense reveals. })); transition.ready.finally(() => { diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 216b6d668a9..d8b5f448d52 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -194,7 +194,7 @@ export const disableLegacyContext = true; export const disableLegacyContextForFunctionComponents = true; // Enable the moveBefore() alternative to insertBefore(). This preserves states of moves. -export const enableMoveBefore = __EXPERIMENTAL__; +export const enableMoveBefore = false; // Disabled caching behavior of `react/cache` in client runtimes. export const disableClientCache = true;