From 2283eb918328249a9aa5b98d5a93dfb307bce79a Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Thu, 24 Jul 2025 15:38:22 -0700 Subject: [PATCH] [compiler] Improve more error messages This PR uses the new diagnostic type for most of the error messages produced in our explicit validation passes (`Validation/` directory). One of the validations produced multiple errors as a hack to showing multiple related locations, which we can now consolidate into a single diagnostic. --- .../src/CompilerError.ts | 15 ++++- .../src/HIR/BuildHIR.ts | 56 ++++++++++++------- .../ValidateLocalsNotReassignedAfterRender.ts | 25 +++++---- ...ValidateNoFreezingKnownMutableFunctions.ts | 30 ++++++---- .../ValidateNoImpureFunctionsInRender.ts | 29 ++++++---- .../Validation/ValidateNoJSXInTryStatement.ts | 18 ++++-- .../Validation/ValidateNoSetStateInEffects.ts | 29 +++++++--- .../Validation/ValidateNoSetStateInRender.ts | 50 +++++++++++------ .../src/Validation/ValidateUseMemo.ts | 53 ++++++++++++------ ...ive-ref-validation-in-use-effect.expect.md | 15 ++--- ...ext-variable-only-chained-assign.expect.md | 8 +-- ...variable-in-function-declaration.expect.md | 8 +-- ...erences-variable-its-assigned-to.expect.md | 8 +-- ...alid-ReactUseMemo-async-callback.expect.md | 6 +- ...-conditional-setState-in-useMemo.expect.md | 14 ++--- ...-argument-mutates-local-variable.expect.md | 15 ++--- ...eassign-local-variable-in-effect.expect.md | 8 +-- ...id-pass-mutable-function-as-prop.expect.md | 15 ++--- ...ssign-local-in-hook-return-value.expect.md | 8 +-- ...eassign-local-variable-in-effect.expect.md | 8 +-- ...-local-variable-in-hook-argument.expect.md | 8 +-- ...n-local-variable-in-jsx-callback.expect.md | 8 +-- ...eturn-mutable-function-from-hook.expect.md | 15 ++--- ...-in-useMemo-indirect-useCallback.expect.md | 8 +-- ...rror.invalid-setState-in-useMemo.expect.md | 14 ++--- ...es-memoizes-with-captures-values.expect.md | 15 ++--- ...nconditional-set-state-in-render.expect.md | 14 ++--- ...r.invalid-useMemo-async-callback.expect.md | 6 +- ...or.invalid-useMemo-callback-args.expect.md | 8 +-- ...ange-shared-inner-outer-function.expect.md | 8 +-- ...ences-later-variable-declaration.expect.md | 8 +-- ...state-in-render-after-loop-break.expect.md | 8 +-- ...l-set-state-in-render-after-loop.expect.md | 8 +-- ...-state-in-render-with-loop-throw.expect.md | 8 +-- ...r.unconditional-set-state-lambda.expect.md | 8 +-- ...tate-nested-function-expressions.expect.md | 8 +-- ...in-catch-in-outer-try-with-catch.expect.md | 2 +- .../invalid-jsx-in-try-with-catch.expect.md | 2 +- ...setState-in-useEffect-transitive.expect.md | 2 +- .../invalid-setState-in-useEffect.expect.md | 2 +- ...n-local-variable-in-jsx-callback.expect.md | 8 +-- 41 files changed, 324 insertions(+), 262 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts index cac897a9269be..c704d44b55c98 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts @@ -59,7 +59,7 @@ export type CompilerDiagnosticDetail = */ { kind: 'error'; - loc: SourceLocation; + loc: SourceLocation | null; message: string; }; @@ -100,6 +100,12 @@ export class CompilerDiagnostic { this.options = options; } + static create( + options: Omit, + ): CompilerDiagnostic { + return new CompilerDiagnostic({...options, details: []}); + } + get category(): CompilerDiagnosticOptions['category'] { return this.options.category; } @@ -113,6 +119,11 @@ export class CompilerDiagnostic { return this.options.suggestions; } + withDetail(detail: CompilerDiagnosticDetail): CompilerDiagnostic { + this.options.details.push(detail); + return this; + } + primaryLocation(): SourceLocation | null { return this.options.details.filter(d => d.kind === 'error')[0]?.loc ?? null; } @@ -127,7 +138,7 @@ export class CompilerDiagnostic { switch (detail.kind) { case 'error': { const loc = detail.loc; - if (typeof loc === 'symbol') { + if (loc == null || typeof loc === 'symbol') { continue; } let codeFrame: string; diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts index 5b5702f19c62b..cd85656de24f8 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts @@ -9,6 +9,7 @@ import {NodePath, Scope} from '@babel/traverse'; import * as t from '@babel/types'; import invariant from 'invariant'; import { + CompilerDiagnostic, CompilerError, CompilerSuggestionOperation, ErrorSeverity, @@ -104,12 +105,18 @@ export function lower( if (param.isIdentifier()) { const binding = builder.resolveIdentifier(param); if (binding.kind !== 'Identifier') { - builder.errors.push({ - reason: `(BuildHIR::lower) Could not find binding for param \`${param.node.name}\``, - severity: ErrorSeverity.Invariant, - loc: param.node.loc ?? null, - suggestions: null, - }); + builder.errors.pushDiagnostic( + CompilerDiagnostic.create({ + category: 'Could not find binding', + description: `[BuildHIR] Could not find binding for param \`${param.node.name}\``, + severity: ErrorSeverity.Invariant, + suggestions: null, + }).withDetail({ + kind: 'error', + loc: param.node.loc ?? null, + message: 'Could not find binding', + }), + ); return; } const place: Place = { @@ -163,12 +170,18 @@ export function lower( 'Assignment', ); } else { - builder.errors.push({ - reason: `(BuildHIR::lower) Handle ${param.node.type} params`, - severity: ErrorSeverity.Todo, - loc: param.node.loc ?? null, - suggestions: null, - }); + builder.errors.pushDiagnostic( + CompilerDiagnostic.create({ + category: `Handle ${param.node.type} parameters`, + description: `[BuildHIR] Add support for ${param.node.type} parameters`, + severity: ErrorSeverity.Todo, + suggestions: null, + }).withDetail({ + kind: 'error', + loc: param.node.loc ?? null, + message: 'Unsupported parameter type', + }), + ); } }); @@ -188,13 +201,18 @@ export function lower( lowerStatement(builder, body); directives = body.get('directives').map(d => d.node.value.value); } else { - builder.errors.push({ - severity: ErrorSeverity.InvalidJS, - reason: `Unexpected function body kind`, - description: `Expected function body to be an expression or a block statement, got \`${body.type}\``, - loc: body.node.loc ?? null, - suggestions: null, - }); + builder.errors.pushDiagnostic( + CompilerDiagnostic.create({ + severity: ErrorSeverity.InvalidJS, + category: `Unexpected function body kind`, + description: `Expected function body to be an expression or a block statement, got \`${body.type}\``, + suggestions: null, + }).withDetail({ + kind: 'error', + loc: body.node.loc ?? null, + message: 'Expected a block statement or expression', + }), + ); } if (builder.errors.hasErrors()) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts index 9c41ebcae19f6..569bbbdc2d6e3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateLocalsNotReassignedAfterRender.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError, Effect} from '..'; +import {CompilerDiagnostic, CompilerError, Effect, ErrorSeverity} from '..'; import {HIRFunction, IdentifierId, Place} from '../HIR'; import { eachInstructionLValue, @@ -28,16 +28,19 @@ export function validateLocalsNotReassignedAfterRender(fn: HIRFunction): void { false, ); if (reassignment !== null) { - CompilerError.throwInvalidReact({ - reason: - 'Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead', - description: - reassignment.identifier.name !== null && - reassignment.identifier.name.kind === 'named' - ? `Variable \`${reassignment.identifier.name.value}\` cannot be reassigned after render` - : '', - loc: reassignment.loc, - }); + const errors = new CompilerError(); + errors.pushDiagnostic( + CompilerDiagnostic.create({ + severity: ErrorSeverity.InvalidReact, + category: 'Cannot reassign a variable after render completes', + description: `Reassigning ${reassignment.identifier.name != null && reassignment.identifier.name.kind === 'named' ? `variable \`${reassignment.identifier.name.value}\`` : 'a variable'} after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead`, + }).withDetail({ + kind: 'error', + loc: reassignment.loc, + message: 'Cannot reassign variable after render completes', + }), + ); + throw errors; } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoFreezingKnownMutableFunctions.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoFreezingKnownMutableFunctions.ts index 573db2f6b7d00..b988183530ee4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoFreezingKnownMutableFunctions.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoFreezingKnownMutableFunctions.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError, Effect, ErrorSeverity} from '..'; +import {CompilerDiagnostic, CompilerError, Effect, ErrorSeverity} from '..'; import { FunctionEffect, HIRFunction, @@ -57,16 +57,24 @@ export function validateNoFreezingKnownMutableFunctions( if (operand.effect === Effect.Freeze) { const effect = contextMutationEffects.get(operand.identifier.id); if (effect != null) { - errors.push({ - reason: `This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead`, - loc: operand.loc, - severity: ErrorSeverity.InvalidReact, - }); - errors.push({ - reason: `The function modifies a local variable here`, - loc: effect.loc, - severity: ErrorSeverity.InvalidReact, - }); + errors.pushDiagnostic( + CompilerDiagnostic.create({ + severity: ErrorSeverity.InvalidReact, + category: 'Cannot modify local variables after render completes', + description: `This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead`, + }) + .withDetail({ + kind: 'error', + loc: operand.loc, + message: + 'This function may (indirectly) reassign or modify local variables after render', + }) + .withDetail({ + kind: 'error', + loc: effect.loc, + message: 'This modifies a local variable', + }), + ); } } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts index 6e88773ecf0bd..85adb79ceb859 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoImpureFunctionsInRender.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError, ErrorSeverity} from '..'; +import {CompilerDiagnostic, CompilerError, ErrorSeverity} from '..'; import {HIRFunction} from '../HIR'; import {getFunctionCallSignature} from '../Inference/InferReferenceEffects'; import {Result} from '../Utils/Result'; @@ -34,17 +34,22 @@ export function validateNoImpureFunctionsInRender( callee.identifier.type, ); if (signature != null && signature.impure === true) { - errors.push({ - reason: - 'Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent)', - description: - signature.canonicalName != null - ? `\`${signature.canonicalName}\` is an impure function whose results may change on every call` - : null, - severity: ErrorSeverity.InvalidReact, - loc: callee.loc, - suggestions: null, - }); + errors.pushDiagnostic( + CompilerDiagnostic.create({ + category: 'Cannot call impure function during render', + description: + (signature.canonicalName != null + ? `\`${signature.canonicalName}\` is an impure function. ` + : '') + + 'Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent)', + severity: ErrorSeverity.InvalidReact, + suggestions: null, + }).withDetail({ + kind: 'error', + loc: callee.loc, + message: 'Cannot call impure function', + }), + ); } } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts index 505302f7d12c2..eea6c0a08e951 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoJSXInTryStatement.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError, ErrorSeverity} from '..'; +import {CompilerDiagnostic, CompilerError, ErrorSeverity} from '..'; import {BlockId, HIRFunction} from '../HIR'; import {Result} from '../Utils/Result'; import {retainWhere} from '../Utils/utils'; @@ -34,11 +34,17 @@ export function validateNoJSXInTryStatement( switch (value.kind) { case 'JsxExpression': case 'JsxFragment': { - errors.push({ - severity: ErrorSeverity.InvalidReact, - reason: `Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)`, - loc: value.loc, - }); + errors.pushDiagnostic( + CompilerDiagnostic.create({ + severity: ErrorSeverity.InvalidReact, + category: 'Avoid constructing JSX within try/catch', + description: `React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)`, + }).withDetail({ + kind: 'error', + loc: value.loc, + message: 'Avoid constructing JSX within try/catch', + }), + ); break; } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInEffects.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInEffects.ts index e9b0fdb8870ee..3c810025a7e5c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInEffects.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInEffects.ts @@ -5,7 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError, ErrorSeverity} from '../CompilerError'; +import { + CompilerDiagnostic, + CompilerError, + ErrorSeverity, +} from '../CompilerError'; import { HIRFunction, IdentifierId, @@ -90,14 +94,21 @@ export function validateNoSetStateInEffects( if (arg !== undefined && arg.kind === 'Identifier') { const setState = setStateFunctions.get(arg.identifier.id); if (setState !== undefined) { - errors.push({ - reason: - 'Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)', - description: null, - severity: ErrorSeverity.InvalidReact, - loc: setState.loc, - suggestions: null, - }); + errors.pushDiagnostic( + CompilerDiagnostic.create({ + category: + 'Calling setState within an effect can trigger cascading renders', + description: + 'Calling setState directly within a useEffect causes cascading renders that can hurt performance, and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)', + severity: ErrorSeverity.InvalidReact, + suggestions: null, + }).withDetail({ + kind: 'error', + loc: setState.loc, + message: + 'Avoid calling setState() in the top-level of an effect', + }), + ); } } } diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts index fc101581b30b0..81209d61c6853 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoSetStateInRender.ts @@ -5,7 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError, ErrorSeverity} from '../CompilerError'; +import { + CompilerDiagnostic, + CompilerError, + ErrorSeverity, +} from '../CompilerError'; import {HIRFunction, IdentifierId, isSetStateType} from '../HIR'; import {computeUnconditionalBlocks} from '../HIR/ComputeUnconditionalBlocks'; import {eachInstructionValueOperand} from '../HIR/visitors'; @@ -122,23 +126,35 @@ function validateNoSetStateInRenderImpl( unconditionalSetStateFunctions.has(callee.identifier.id) ) { if (activeManualMemoId !== null) { - errors.push({ - reason: - 'Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState)', - description: null, - severity: ErrorSeverity.InvalidReact, - loc: callee.loc, - suggestions: null, - }); + errors.pushDiagnostic( + CompilerDiagnostic.create({ + category: + 'Calling setState from useMemo may trigger an infinite loop', + description: + 'Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState)', + severity: ErrorSeverity.InvalidReact, + suggestions: null, + }).withDetail({ + kind: 'error', + loc: callee.loc, + message: 'Found setState() within useMemo()', + }), + ); } else if (unconditionalBlocks.has(block.id)) { - errors.push({ - reason: - 'This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState)', - description: null, - severity: ErrorSeverity.InvalidReact, - loc: callee.loc, - suggestions: null, - }); + errors.pushDiagnostic( + CompilerDiagnostic.create({ + category: + 'Calling setState during render may trigger an infinite loop', + description: + 'Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState)', + severity: ErrorSeverity.InvalidReact, + suggestions: null, + }).withDetail({ + kind: 'error', + loc: callee.loc, + message: 'Found setState() within useMemo()', + }), + ); } } break; diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts index fd4d781ef3c6f..7c83e65dff0fd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateUseMemo.ts @@ -5,7 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import {CompilerError, ErrorSeverity} from '..'; +import { + CompilerDiagnostic, + CompilerError, + ErrorSeverity, +} from '../CompilerError'; import {FunctionExpression, HIRFunction, IdentifierId} from '../HIR'; import {Result} from '../Utils/Result'; @@ -63,24 +67,41 @@ export function validateUseMemo(fn: HIRFunction): Result { } if (body.loweredFunc.func.params.length > 0) { - errors.push({ - severity: ErrorSeverity.InvalidReact, - reason: 'useMemo callbacks may not accept any arguments', - description: null, - loc: body.loc, - suggestions: null, - }); + const firstParam = body.loweredFunc.func.params[0]; + const loc = + firstParam.kind === 'Identifier' + ? firstParam.loc + : firstParam.place.loc; + errors.pushDiagnostic( + CompilerDiagnostic.create({ + severity: ErrorSeverity.InvalidReact, + category: 'useMemo() callbacks may not accept parameters', + description: + 'useMemo() callbacks are called by React to cache calculations across re-renders. They should not take parameters. Instead, directly reference the props, state, or local variables needed for the computation.', + suggestions: null, + }).withDetail({ + kind: 'error', + loc, + message: '', + }), + ); } if (body.loweredFunc.func.async || body.loweredFunc.func.generator) { - errors.push({ - severity: ErrorSeverity.InvalidReact, - reason: - 'useMemo callbacks may not be async or generator functions', - description: null, - loc: body.loc, - suggestions: null, - }); + errors.pushDiagnostic( + CompilerDiagnostic.create({ + severity: ErrorSeverity.InvalidReact, + category: + 'useMemo callbacks may not be async or generator functions', + description: + 'useMemo() callbacks are called once and must synchronously return a value', + suggestions: null, + }).withDetail({ + kind: 'error', + loc: body.loc, + message: 'Async and generator functions are not supported', + }), + ); } break; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bug-old-inference-false-positive-ref-validation-in-use-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bug-old-inference-false-positive-ref-validation-in-use-effect.expect.md index 84370796a6724..a33ff7ce76582 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bug-old-inference-false-positive-ref-validation-in-use-effect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bug-old-inference-false-positive-ref-validation-in-use-effect.expect.md @@ -36,8 +36,10 @@ function Component() { ## Error ``` -Found 2 errors: -Error: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead +Found 1 error: +Error: Cannot modify local variables after render completes + +This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead error.bug-old-inference-false-positive-ref-validation-in-use-effect.ts:20:12 18 | ); @@ -51,24 +53,19 @@ error.bug-old-inference-false-positive-ref-validation-in-use-effect.ts:20:12 > 23 | } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > 24 | }, [update]); - | ^^^^ This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^^^^ This function may (indirectly) reassign or modify local variables after render 25 | 26 | return 'ok'; 27 | } - -Error: The function modifies a local variable here - error.bug-old-inference-false-positive-ref-validation-in-use-effect.ts:14:6 12 | ...partialParams, 13 | }; > 14 | nextParams.param = 'value'; - | ^^^^^^^^^^ The function modifies a local variable here + | ^^^^^^^^^^ This modifies a local variable 15 | console.log(nextParams); 16 | }, 17 | [params] - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.context-variable-only-chained-assign.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.context-variable-only-chained-assign.expect.md index de50b21543742..408537c5b8a39 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.context-variable-only-chained-assign.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.context-variable-only-chained-assign.expect.md @@ -29,20 +29,18 @@ export const FIXTURE_ENTRYPOINT = { ``` Found 1 error: -Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead +Error: Cannot reassign a variable after render completes -Variable `x` cannot be reassigned after render. +Reassigning variable `x` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead error.context-variable-only-chained-assign.ts:10:19 8 | }; 9 | const fn2 = () => { > 10 | const copy2 = (x = 4); - | ^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^ Cannot reassign variable after render completes 11 | return [invoke(fn1), copy2, identity(copy2)]; 12 | }; 13 | return invoke(fn2); - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.declare-reassign-variable-in-function-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.declare-reassign-variable-in-function-declaration.expect.md index 6823db842d2a1..2c1c7657f731f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.declare-reassign-variable-in-function-declaration.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.declare-reassign-variable-in-function-declaration.expect.md @@ -18,20 +18,18 @@ function Component() { ``` Found 1 error: -Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead +Error: Cannot reassign a variable after render completes -Variable `x` cannot be reassigned after render. +Reassigning variable `x` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead error.declare-reassign-variable-in-function-declaration.ts:4:4 2 | let x = null; 3 | function foo() { > 4 | x = 9; - | ^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^ Cannot reassign variable after render completes 5 | } 6 | const y = bar(foo); 7 | return ; - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.expect.md index 47af99524836a..fba4e272ee9d9 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.function-expression-references-variable-its-assigned-to.expect.md @@ -16,20 +16,18 @@ function Component() { ``` Found 1 error: -Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead +Error: Cannot reassign a variable after render completes -Variable `callback` cannot be reassigned after render. +Reassigning variable `callback` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead error.function-expression-references-variable-its-assigned-to.ts:3:4 1 | function Component() { 2 | let callback = () => { > 3 | callback = null; - | ^^^^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^^^^^^^^ Cannot reassign variable after render completes 4 | }; 5 | return
; 6 | } - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ReactUseMemo-async-callback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ReactUseMemo-async-callback.expect.md index 1c5c92d2c305a..2f8fd0e671727 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ReactUseMemo-async-callback.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ReactUseMemo-async-callback.expect.md @@ -18,6 +18,8 @@ function component(a, b) { Found 1 error: Error: useMemo callbacks may not be async or generator functions +useMemo() callbacks are called once and must synchronously return a value + error.invalid-ReactUseMemo-async-callback.ts:2:24 1 | function component(a, b) { > 2 | let x = React.useMemo(async () => { @@ -25,12 +27,10 @@ error.invalid-ReactUseMemo-async-callback.ts:2:24 > 3 | await a; | ^^^^^^^^^^^^ > 4 | }, []); - | ^^^^ useMemo callbacks may not be async or generator functions + | ^^^^ Async and generator functions are not supported 5 | return x; 6 | } 7 | - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-setState-in-useMemo.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-setState-in-useMemo.expect.md index 5af5db112fb1b..0334a33cfee91 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-setState-in-useMemo.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-conditional-setState-in-useMemo.expect.md @@ -23,30 +23,30 @@ function Component({item, cond}) { ``` Found 2 errors: -Error: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) +Error: Calling setState from useMemo may trigger an infinite loop + +Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState) error.invalid-conditional-setState-in-useMemo.ts:7:6 5 | useMemo(() => { 6 | if (cond) { > 7 | setPrevItem(item); - | ^^^^^^^^^^^ Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) + | ^^^^^^^^^^^ Found setState() within useMemo() 8 | setState(0); 9 | } 10 | }, [cond, key, init]); +Error: Calling setState from useMemo may trigger an infinite loop - -Error: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) +Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState) error.invalid-conditional-setState-in-useMemo.ts:8:6 6 | if (cond) { 7 | setPrevItem(item); > 8 | setState(0); - | ^^^^^^^^ Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) + | ^^^^^^^^ Found setState() within useMemo() 9 | } 10 | }, [cond, key, init]); 11 | - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-hook-function-argument-mutates-local-variable.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-hook-function-argument-mutates-local-variable.expect.md index d9e0ed390bbfb..8d9be03b42773 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-hook-function-argument-mutates-local-variable.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-hook-function-argument-mutates-local-variable.expect.md @@ -17,8 +17,10 @@ function useFoo() { ## Error ``` -Found 2 errors: -Error: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead +Found 1 error: +Error: Cannot modify local variables after render completes + +This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead error.invalid-hook-function-argument-mutates-local-variable.ts:5:10 3 | function useFoo() { @@ -28,23 +30,18 @@ error.invalid-hook-function-argument-mutates-local-variable.ts:5:10 > 6 | cache.set('key', 'value'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > 7 | }); - | ^^^^ This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^^^^ This function may (indirectly) reassign or modify local variables after render 8 | } 9 | - -Error: The function modifies a local variable here - error.invalid-hook-function-argument-mutates-local-variable.ts:6:4 4 | const cache = new Map(); 5 | useHook(() => { > 6 | cache.set('key', 'value'); - | ^^^^^ The function modifies a local variable here + | ^^^^^ This modifies a local variable 7 | }); 8 | } 9 | - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-nested-function-reassign-local-variable-in-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-nested-function-reassign-local-variable-in-effect.expect.md index 025560bd50515..073a15b7f52c4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-nested-function-reassign-local-variable-in-effect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-nested-function-reassign-local-variable-in-effect.expect.md @@ -47,20 +47,18 @@ function Component() { ``` Found 1 error: -Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead +Error: Cannot reassign a variable after render completes -Variable `local` cannot be reassigned after render. +Reassigning variable `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead error.invalid-nested-function-reassign-local-variable-in-effect.ts:7:6 5 | // Create the reassignment function inside another function, then return it 6 | const reassignLocal = newValue => { > 7 | local = newValue; - | ^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^^^^^ Cannot reassign variable after render completes 8 | }; 9 | return reassignLocal; 10 | }; - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-pass-mutable-function-as-prop.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-pass-mutable-function-as-prop.expect.md index 259a57c3e7c37..50d3a9e668761 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-pass-mutable-function-as-prop.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-pass-mutable-function-as-prop.expect.md @@ -17,30 +17,27 @@ function Component() { ## Error ``` -Found 2 errors: -Error: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead +Found 1 error: +Error: Cannot modify local variables after render completes + +This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead error.invalid-pass-mutable-function-as-prop.ts:7:18 5 | cache.set('key', 'value'); 6 | }; > 7 | return ; - | ^^ This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^^ This function may (indirectly) reassign or modify local variables after render 8 | } 9 | - -Error: The function modifies a local variable here - error.invalid-pass-mutable-function-as-prop.ts:5:4 3 | const cache = new Map(); 4 | const fn = () => { > 5 | cache.set('key', 'value'); - | ^^^^^ The function modifies a local variable here + | ^^^^^ This modifies a local variable 6 | }; 7 | return ; 8 | } - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-in-hook-return-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-in-hook-return-value.expect.md index a8eee83aa10b2..02750e447205a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-in-hook-return-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-in-hook-return-value.expect.md @@ -16,20 +16,18 @@ function useFoo() { ``` Found 1 error: -Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead +Error: Cannot reassign a variable after render completes -Variable `x` cannot be reassigned after render. +Reassigning variable `x` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead error.invalid-reassign-local-in-hook-return-value.ts:4:4 2 | let x = 0; 3 | return value => { > 4 | x = value; - | ^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^ Cannot reassign variable after render completes 5 | }; 6 | } 7 | - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-variable-in-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-variable-in-effect.expect.md index 47e3509dbfd4d..3f8a81c106b02 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-variable-in-effect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-variable-in-effect.expect.md @@ -48,20 +48,18 @@ function Component() { ``` Found 1 error: -Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead +Error: Cannot reassign a variable after render completes -Variable `local` cannot be reassigned after render. +Reassigning variable `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead error.invalid-reassign-local-variable-in-effect.ts:7:4 5 | 6 | const reassignLocal = newValue => { > 7 | local = newValue; - | ^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^^^^^ Cannot reassign variable after render completes 8 | }; 9 | 10 | const onMount = newValue => { - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-variable-in-hook-argument.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-variable-in-hook-argument.expect.md index e9774d0c9932a..8bf6cd1ccc6b7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-variable-in-hook-argument.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-variable-in-hook-argument.expect.md @@ -49,20 +49,18 @@ function Component() { ``` Found 1 error: -Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead +Error: Cannot reassign a variable after render completes -Variable `local` cannot be reassigned after render. +Reassigning variable `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead error.invalid-reassign-local-variable-in-hook-argument.ts:8:4 6 | 7 | const reassignLocal = newValue => { > 8 | local = newValue; - | ^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^^^^^ Cannot reassign variable after render completes 9 | }; 10 | 11 | const callback = newValue => { - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-variable-in-jsx-callback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-variable-in-jsx-callback.expect.md index cd565964d5532..ea7fd3d1d7b07 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-variable-in-jsx-callback.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-local-variable-in-jsx-callback.expect.md @@ -42,20 +42,18 @@ function Component() { ``` Found 1 error: -Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead +Error: Cannot reassign a variable after render completes -Variable `local` cannot be reassigned after render. +Reassigning variable `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead error.invalid-reassign-local-variable-in-jsx-callback.ts:5:4 3 | 4 | const reassignLocal = newValue => { > 5 | local = newValue; - | ^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^^^^^ Cannot reassign variable after render completes 6 | }; 7 | 8 | const onClick = newValue => { - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-return-mutable-function-from-hook.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-return-mutable-function-from-hook.expect.md index eb499847a3669..767c05ec73b00 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-return-mutable-function-from-hook.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-return-mutable-function-from-hook.expect.md @@ -19,8 +19,10 @@ function useFoo() { ## Error ``` -Found 2 errors: -Error: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead +Found 1 error: +Error: Cannot modify local variables after render completes + +This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead error.invalid-return-mutable-function-from-hook.ts:7:9 5 | useHook(); // for inference to kick in @@ -30,23 +32,18 @@ error.invalid-return-mutable-function-from-hook.ts:7:9 > 8 | cache.set('key', 'value'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > 9 | }; - | ^^^^ This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^^^^ This function may (indirectly) reassign or modify local variables after render 10 | } 11 | - -Error: The function modifies a local variable here - error.invalid-return-mutable-function-from-hook.ts:8:4 6 | const cache = new Map(); 7 | return () => { > 8 | cache.set('key', 'value'); - | ^^^^^ The function modifies a local variable here + | ^^^^^ This modifies a local variable 9 | }; 10 | } 11 | - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useMemo-indirect-useCallback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useMemo-indirect-useCallback.expect.md index c29483ca98156..156c0aff8762c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useMemo-indirect-useCallback.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useMemo-indirect-useCallback.expect.md @@ -27,18 +27,18 @@ function useKeyedState({key, init}) { ``` Found 1 error: -Error: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) +Error: Calling setState from useMemo may trigger an infinite loop + +Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState) error.invalid-setState-in-useMemo-indirect-useCallback.ts:13:4 11 | 12 | useMemo(() => { > 13 | fn(); - | ^^ Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) + | ^^ Found setState() within useMemo() 14 | }, [key, init]); 15 | 16 | return state; - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useMemo.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useMemo.expect.md index 8be4ac12a297c..1cdd24d7311b5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useMemo.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-useMemo.expect.md @@ -21,30 +21,30 @@ function useKeyedState({key, init}) { ``` Found 2 errors: -Error: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) +Error: Calling setState from useMemo may trigger an infinite loop + +Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState) error.invalid-setState-in-useMemo.ts:6:4 4 | 5 | useMemo(() => { > 6 | setPrevKey(key); - | ^^^^^^^^^^ Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) + | ^^^^^^^^^^ Found setState() within useMemo() 7 | setState(init); 8 | }, [key, init]); 9 | +Error: Calling setState from useMemo may trigger an infinite loop - -Error: Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) +Each time the memo callback is evaluated it will change state. This can cause a memoization dependency to change, running the memo function again and causing an infinite loop. Instead of setting state in useMemo(), prefer deriving the value during render. (https://react.dev/reference/react/useState) error.invalid-setState-in-useMemo.ts:7:4 5 | useMemo(() => { 6 | setPrevKey(key); > 7 | setState(init); - | ^^^^^^^^ Calling setState from useMemo may trigger an infinite loop. (https://react.dev/reference/react/useState) + | ^^^^^^^^ Found setState() within useMemo() 8 | }, [key, init]); 9 | 10 | return state; - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md index af590101c0fc1..88788bbfee3ca 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-uncalled-function-capturing-mutable-values-memoizes-with-captures-values.expect.md @@ -47,8 +47,10 @@ hook useMemoMap( ## Error ``` -Found 2 errors: -Error: This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead +Found 1 error: +Error: Cannot modify local variables after render completes + +This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead undefined:21:9 19 | map: TInput => TOutput @@ -86,23 +88,18 @@ undefined:21:9 > 36 | }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > 37 | }, [map]); - | ^^^^^^^^^^^^ This argument is a function which may reassign or mutate local variables after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^^^^^^^^^^^^ This function may (indirectly) reassign or modify local variables after render 38 | } 39 | - -Error: The function modifies a local variable here - undefined:33:8 31 | if (output == null) { 32 | output = map(input); > 33 | cache.set(input, output); - | ^^^^^ The function modifies a local variable here + | ^^^^^ This modifies a local variable 34 | } 35 | return output; 36 | }; - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-unconditional-set-state-in-render.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-unconditional-set-state-in-render.expect.md index 0c15a0f632f45..82138d8a5a859 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-unconditional-set-state-in-render.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-unconditional-set-state-in-render.expect.md @@ -20,30 +20,30 @@ function Component(props) { ``` Found 2 errors: -Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) +Error: Calling setState during render may trigger an infinite loop + +Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState) error.invalid-unconditional-set-state-in-render.ts:6:2 4 | const aliased = setX; 5 | > 6 | setX(1); - | ^^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) + | ^^^^ Found setState() within useMemo() 7 | aliased(2); 8 | 9 | return x; +Error: Calling setState during render may trigger an infinite loop - -Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) +Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState) error.invalid-unconditional-set-state-in-render.ts:7:2 5 | 6 | setX(1); > 7 | aliased(2); - | ^^^^^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) + | ^^^^^^^ Found setState() within useMemo() 8 | 9 | return x; 10 | } - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-useMemo-async-callback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-useMemo-async-callback.expect.md index 272ed7ab8c0f0..7fc99985f1a50 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-useMemo-async-callback.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-useMemo-async-callback.expect.md @@ -18,6 +18,8 @@ function component(a, b) { Found 1 error: Error: useMemo callbacks may not be async or generator functions +useMemo() callbacks are called once and must synchronously return a value + error.invalid-useMemo-async-callback.ts:2:18 1 | function component(a, b) { > 2 | let x = useMemo(async () => { @@ -25,12 +27,10 @@ error.invalid-useMemo-async-callback.ts:2:18 > 3 | await a; | ^^^^^^^^^^^^ > 4 | }, []); - | ^^^^ useMemo callbacks may not be async or generator functions + | ^^^^ Async and generator functions are not supported 5 | return x; 6 | } 7 | - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-useMemo-callback-args.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-useMemo-callback-args.expect.md index 6c37090917550..97996884b25f4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-useMemo-callback-args.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-useMemo-callback-args.expect.md @@ -14,17 +14,17 @@ function component(a, b) { ``` Found 1 error: -Error: useMemo callbacks may not accept any arguments +Error: useMemo() callbacks may not accept parameters + +useMemo() callbacks are called by React to cache calculations across re-renders. They should not take parameters. Instead, directly reference the props, state, or local variables needed for the computation. error.invalid-useMemo-callback-args.ts:2:18 1 | function component(a, b) { > 2 | let x = useMemo(c => a, []); - | ^^^^^^ useMemo callbacks may not accept any arguments + | ^ 3 | return x; 4 | } 5 | - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutable-range-shared-inner-outer-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutable-range-shared-inner-outer-function.expect.md index 151c1a20c7254..e284cb78145e1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutable-range-shared-inner-outer-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.mutable-range-shared-inner-outer-function.expect.md @@ -33,20 +33,18 @@ export const FIXTURE_ENTRYPOINT = { ``` Found 1 error: -Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead +Error: Cannot reassign a variable after render completes -Variable `a` cannot be reassigned after render. +Reassigning variable `a` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead error.mutable-range-shared-inner-outer-function.ts:8:6 6 | const f = () => { 7 | if (cond) { > 8 | a = {}; - | ^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^ Cannot reassign variable after render completes 9 | b = []; 10 | } else { 11 | a = {}; - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md index ce4814b172ef4..f151b624e9a95 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-function-expression-references-later-variable-declaration.expect.md @@ -18,20 +18,18 @@ function Component() { ``` Found 1 error: -Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead +Error: Cannot reassign a variable after render completes -Variable `onClick` cannot be reassigned after render. +Reassigning variable `onClick` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead error.todo-function-expression-references-later-variable-declaration.ts:3:4 1 | function Component() { 2 | let callback = () => { > 3 | onClick = () => {}; - | ^^^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^^^^^^^ Cannot reassign variable after render completes 4 | }; 5 | let onClick; 6 | - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-after-loop-break.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-after-loop-break.expect.md index a90a9fd423beb..1a51d17fb491e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-after-loop-break.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-after-loop-break.expect.md @@ -23,18 +23,18 @@ function Component(props) { ``` Found 1 error: -Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) +Error: Calling setState during render may trigger an infinite loop + +Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState) error.unconditional-set-state-in-render-after-loop-break.ts:11:2 9 | } 10 | } > 11 | setState(true); - | ^^^^^^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) + | ^^^^^^^^ Found setState() within useMemo() 12 | return state; 13 | } 14 | - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-after-loop.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-after-loop.expect.md index 65c1a8caae8b7..903ee395f4ab3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-after-loop.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-after-loop.expect.md @@ -18,18 +18,18 @@ function Component(props) { ``` Found 1 error: -Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) +Error: Calling setState during render may trigger an infinite loop + +Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState) error.unconditional-set-state-in-render-after-loop.ts:6:2 4 | for (const _ of props) { 5 | } > 6 | setState(true); - | ^^^^^^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) + | ^^^^^^^^ Found setState() within useMemo() 7 | return state; 8 | } 9 | - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-with-loop-throw.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-with-loop-throw.expect.md index c2f70e67caeaa..de829f66fc2d3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-with-loop-throw.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-in-render-with-loop-throw.expect.md @@ -23,18 +23,18 @@ function Component(props) { ``` Found 1 error: -Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) +Error: Calling setState during render may trigger an infinite loop + +Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState) error.unconditional-set-state-in-render-with-loop-throw.ts:11:2 9 | } 10 | } > 11 | setState(true); - | ^^^^^^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) + | ^^^^^^^^ Found setState() within useMemo() 12 | return state; 13 | } 14 | - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-lambda.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-lambda.expect.md index 12332ae299b06..447183b79c898 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-lambda.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-lambda.expect.md @@ -21,18 +21,18 @@ function Component(props) { ``` Found 1 error: -Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) +Error: Calling setState during render may trigger an infinite loop + +Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState) error.unconditional-set-state-lambda.ts:8:2 6 | setX(1); 7 | }; > 8 | foo(); - | ^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) + | ^^^ Found setState() within useMemo() 9 | 10 | return [x]; 11 | } - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-nested-function-expressions.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-nested-function-expressions.expect.md index 4d43240997b32..dcd643edb241f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-nested-function-expressions.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.unconditional-set-state-nested-function-expressions.expect.md @@ -29,18 +29,18 @@ function Component(props) { ``` Found 1 error: -Error: This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) +Error: Calling setState during render may trigger an infinite loop + +Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState) error.unconditional-set-state-nested-function-expressions.ts:16:2 14 | bar(); 15 | }; > 16 | baz(); - | ^^^ This is an unconditional set state during render, which will trigger an infinite loop. (https://react.dev/reference/react/useState) + | ^^^ Found setState() within useMemo() 17 | 18 | return [x]; 19 | } - - ``` \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md index 9f4d85de707d4..6583f0a4e2dcf 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.expect.md @@ -65,7 +65,7 @@ function Component(props) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"reason":"Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","description":null,"severity":"InvalidReact","loc":{"start":{"line":11,"column":11,"index":222},"end":{"line":11,"column":32,"index":243},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"}}},"fnLoc":null} +{"kind":"CompileError","detail":{"options":{"severity":"InvalidReact","category":"Avoid constructing JSX within try/catch","description":"React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","details":[{"kind":"error","loc":{"start":{"line":11,"column":11,"index":222},"end":{"line":11,"column":32,"index":243},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"},"message":"Avoid constructing JSX within try/catch"}]}},"fnLoc":null} {"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":91},"end":{"line":17,"column":1,"index":298},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md index 9fe89f25eeea9..b6e9e87ada427 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.expect.md @@ -42,7 +42,7 @@ function Component(props) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"reason":"Unexpected JSX element within a try statement. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","description":null,"severity":"InvalidReact","loc":{"start":{"line":5,"column":9,"index":104},"end":{"line":5,"column":16,"index":111},"filename":"invalid-jsx-in-try-with-catch.ts"}}},"fnLoc":null} +{"kind":"CompileError","detail":{"options":{"severity":"InvalidReact","category":"Avoid constructing JSX within try/catch","description":"React does not immediately render components when JSX is rendered, so any errors from this component will not be caught by the try/catch. To catch errors in rendering a given component, wrap that component in an error boundary. (https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary)","details":[{"kind":"error","loc":{"start":{"line":5,"column":9,"index":104},"end":{"line":5,"column":16,"index":111},"filename":"invalid-jsx-in-try-with-catch.ts"},"message":"Avoid constructing JSX within try/catch"}]}},"fnLoc":null} {"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":49},"end":{"line":10,"column":1,"index":160},"filename":"invalid-jsx-in-try-with-catch.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md index b09fd352f24f7..4e9e1f2c3a799 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.expect.md @@ -65,7 +65,7 @@ function _temp(s) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":13,"column":4,"index":265},"end":{"line":13,"column":5,"index":266},"filename":"invalid-setState-in-useEffect-transitive.ts","identifierName":"g"}}},"fnLoc":null} +{"kind":"CompileError","detail":{"options":{"category":"Calling setState within an effect can trigger cascading renders","description":"Calling setState directly within a useEffect causes cascading renders that can hurt performance, and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","severity":"InvalidReact","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":13,"column":4,"index":265},"end":{"line":13,"column":5,"index":266},"filename":"invalid-setState-in-useEffect-transitive.ts","identifierName":"g"},"message":"Avoid calling setState() in the top-level of an effect"}]}},"fnLoc":null} {"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":92},"end":{"line":16,"column":1,"index":293},"filename":"invalid-setState-in-useEffect-transitive.ts"},"fnName":"Component","memoSlots":2,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md index 41390e5ce0a22..8d902c2c7d649 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.expect.md @@ -45,7 +45,7 @@ function _temp(s) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"reason":"Calling setState directly within a useEffect causes cascading renders and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":7,"column":4,"index":180},"end":{"line":7,"column":12,"index":188},"filename":"invalid-setState-in-useEffect.ts","identifierName":"setState"}}},"fnLoc":null} +{"kind":"CompileError","detail":{"options":{"category":"Calling setState within an effect can trigger cascading renders","description":"Calling setState directly within a useEffect causes cascading renders that can hurt performance, and is not recommended. Consider alternatives to useEffect. (https://react.dev/learn/you-might-not-need-an-effect)","severity":"InvalidReact","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":7,"column":4,"index":180},"end":{"line":7,"column":12,"index":188},"filename":"invalid-setState-in-useEffect.ts","identifierName":"setState"},"message":"Avoid calling setState() in the top-level of an effect"}]}},"fnLoc":null} {"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":92},"end":{"line":10,"column":1,"index":225},"filename":"invalid-setState-in-useEffect.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.invalid-reassign-local-variable-in-jsx-callback.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.invalid-reassign-local-variable-in-jsx-callback.expect.md index cca5515ab78a7..2050ea107c64d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.invalid-reassign-local-variable-in-jsx-callback.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/new-mutability/error.invalid-reassign-local-variable-in-jsx-callback.expect.md @@ -43,20 +43,18 @@ function Component() { ``` Found 1 error: -Error: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead +Error: Cannot reassign a variable after render completes -Variable `local` cannot be reassigned after render. +Reassigning variable `local` after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead error.invalid-reassign-local-variable-in-jsx-callback.ts:6:4 4 | 5 | const reassignLocal = newValue => { > 6 | local = newValue; - | ^^^^^ Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead + | ^^^^^ Cannot reassign variable after render completes 7 | }; 8 | 9 | const onClick = newValue => { - - ``` \ No newline at end of file