From 8c19a8463a6cb2c4f44a0e43ef66393aae7ff933 Mon Sep 17 00:00:00 2001 From: Jorge Cabiedes Acosta Date: Thu, 11 Dec 2025 16:16:17 -0800 Subject: [PATCH] [compiler] Only run validations with env.logErrors on outputMode: 'lint' Summary: These validations are not essential for compilation, with this we only run that logic when outputMode is 'lint' Test Plan: Update fixtures and run tests --- .../src/Entrypoint/Pipeline.ts | 15 +++- ...ed-state-conditionally-in-effect.expect.md | 51 ++++------- .../derived-state-conditionally-in-effect.js | 2 +- ...derived-state-from-default-props.expect.md | 44 +++------ .../derived-state-from-default-props.js | 2 +- ...state-from-local-state-in-effect.expect.md | 51 +++-------- ...erived-state-from-local-state-in-effect.js | 2 +- ...-local-state-and-component-scope.expect.md | 79 ++++------------ ...om-prop-local-state-and-component-scope.js | 2 +- ...ter-call-outside-effect-no-error.expect.md | 55 +++--------- ...rop-setter-call-outside-effect-no-error.js | 2 +- ...d-state-from-prop-setter-ternary.expect.md | 38 ++------ .../derived-state-from-prop-setter-ternary.js | 2 +- ...ter-used-outside-effect-no-error.expect.md | 56 +++--------- ...rop-setter-used-outside-effect-no-error.js | 2 +- ...state-from-prop-with-side-effect.expect.md | 44 +++------ ...erived-state-from-prop-with-side-effect.js | 2 +- ...tate-from-ref-and-state-no-error.expect.md | 40 +++------ ...rived-state-from-ref-and-state-no-error.js | 2 +- ...ect-contains-local-function-call.expect.md | 56 ++++-------- .../effect-contains-local-function-call.js | 2 +- ...ains-prop-function-call-no-error.expect.md | 48 +++------- ...ct-contains-prop-function-call-no-error.js | 2 +- ...t-used-in-dep-array-still-errors.expect.md | 45 +++------- .../effect-used-in-dep-array-still-errors.js | 2 +- ...ing-on-derived-computation-value.expect.md | 49 ++++------ ...-depending-on-derived-computation-value.js | 2 +- ...th-global-function-call-no-error.expect.md | 41 +++------ ...fect-with-global-function-call-no-error.js | 2 +- ...rops-setstate-in-effect-no-error.expect.md | 44 ++------- .../from-props-setstate-in-effect-no-error.js | 2 +- ...on-expression-mutation-edge-case.expect.md | 90 +++++++------------ .../function-expression-mutation-edge-case.js | 2 +- ...id-derived-computation-in-effect.expect.md | 42 +++------ .../invalid-derived-computation-in-effect.js | 2 +- ...erived-state-from-computed-props.expect.md | 43 +++------ ...valid-derived-state-from-computed-props.js | 2 +- ...ed-state-from-destructured-props.expect.md | 43 +++------ ...d-derived-state-from-destructured-props.js | 2 +- ...f-conditional-in-effect-no-error.expect.md | 48 +++------- .../ref-conditional-in-effect-no-error.js | 2 +- ...m-prop-no-show-in-data-flow-tree.expect.md | 50 ++++------- ...ved-from-prop-no-show-in-data-flow-tree.js | 2 +- ...-catch-in-outer-try-with-finally.expect.md | 2 +- ...-jsx-in-catch-in-outer-try-with-finally.js | 2 +- ...-invalid-jsx-in-try-with-finally.expect.md | 2 +- ...or.todo-invalid-jsx-in-try-with-finally.js | 2 +- ...in-catch-in-outer-try-with-catch.expect.md | 29 ++---- ...id-jsx-in-catch-in-outer-try-with-catch.js | 2 +- .../invalid-jsx-in-try-with-catch.expect.md | 18 ++-- .../compiler/invalid-jsx-in-try-with-catch.js | 2 +- ...setState-in-useEffect-transitive.expect.md | 44 +++------ ...nvalid-setState-in-useEffect-transitive.js | 2 +- ...-in-useEffect-via-useEffectEvent.expect.md | 43 +++------ ...etState-in-useEffect-via-useEffectEvent.js | 2 +- .../invalid-setState-in-useEffect.expect.md | 25 ++---- .../compiler/invalid-setState-in-useEffect.js | 2 +- ...-constructed-component-in-render.expect.md | 28 ++---- ...mically-constructed-component-in-render.js | 2 +- ...ly-construct-component-in-render.expect.md | 20 ++--- ...namically-construct-component-in-render.js | 2 +- ...y-constructed-component-function.expect.md | 22 ++--- ...amically-constructed-component-function.js | 2 +- ...onstructed-component-method-call.expect.md | 29 ++---- ...cally-constructed-component-method-call.js | 2 +- ...ically-constructed-component-new.expect.md | 20 ++--- ...d-dynamically-constructed-component-new.js | 2 +- ...-set-state-in-useEffect-from-ref.expect.md | 26 ++---- .../valid-set-state-in-useEffect-from-ref.js | 2 +- ...te-in-effect-from-ref-arithmetic.expect.md | 33 ++----- ...-setState-in-effect-from-ref-arithmetic.js | 2 +- ...e-in-effect-from-ref-array-index.expect.md | 34 ++----- ...setState-in-effect-from-ref-array-index.js | 2 +- ...in-effect-from-ref-function-call.expect.md | 37 +++----- ...tState-in-effect-from-ref-function-call.js | 2 +- ...seEffect-controlled-by-ref-value.expect.md | 45 ++++------ ...te-in-useEffect-controlled-by-ref-value.js | 2 +- ...in-useEffect-listener-transitive.expect.md | 23 ++--- ...tState-in-useEffect-listener-transitive.js | 2 +- ...d-setState-in-useEffect-listener.expect.md | 18 ++-- .../valid-setState-in-useEffect-listener.js | 2 +- ...tate-in-useLayoutEffect-from-ref.expect.md | 26 ++---- ...id-setState-in-useLayoutEffect-from-ref.js | 2 +- 83 files changed, 490 insertions(+), 1188 deletions(-) diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index ab5ac590d905..30d665227159 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -279,17 +279,20 @@ function runWithEnvironment( validateNoSetStateInRender(hir).unwrap(); } - if (env.config.validateNoDerivedComputationsInEffects_exp) { + if ( + env.config.validateNoDerivedComputationsInEffects_exp && + env.outputMode === 'lint' + ) { env.logErrors(validateNoDerivedComputationsInEffects_exp(hir)); } else if (env.config.validateNoDerivedComputationsInEffects) { validateNoDerivedComputationsInEffects(hir); } - if (env.config.validateNoSetStateInEffects) { + if (env.config.validateNoSetStateInEffects && env.outputMode === 'lint') { env.logErrors(validateNoSetStateInEffects(hir, env)); } - if (env.config.validateNoJSXInTryStatements) { + if (env.config.validateNoJSXInTryStatements && env.outputMode === 'lint') { env.logErrors(validateNoJSXInTryStatement(hir)); } @@ -320,7 +323,11 @@ function runWithEnvironment( value: hir, }); - if (env.enableValidations && env.config.validateStaticComponents) { + if ( + env.enableValidations && + env.config.validateStaticComponents && + env.outputMode === 'lint' + ) { env.logErrors(validateStaticComponents(hir)); } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-conditionally-in-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-conditionally-in-effect.expect.md index a3623dc299c2..fa5ae370e67e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-conditionally-in-effect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-conditionally-in-effect.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({value, enabled}) { @@ -29,42 +29,21 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; -function Component(t0) { - const $ = _c(6); - const { value, enabled } = t0; +function Component({ value, enabled }) { const [localValue, setLocalValue] = useState(""); - let t1; - let t2; - if ($[0] !== enabled || $[1] !== value) { - t1 = () => { - if (enabled) { - setLocalValue(value); - } else { - setLocalValue("disabled"); - } - }; - t2 = [value, enabled]; - $[0] = enabled; - $[1] = value; - $[2] = t1; - $[3] = t2; - } else { - t1 = $[2]; - t2 = $[3]; - } - useEffect(t1, t2); - let t3; - if ($[4] !== localValue) { - t3 =
{localValue}
; - $[4] = localValue; - $[5] = t3; - } else { - t3 = $[5]; - } - return t3; + + useEffect(() => { + if (enabled) { + setLocalValue(value); + } else { + setLocalValue("disabled"); + } + }, [value, enabled]); + + return
{localValue}
; } export const FIXTURE_ENTRYPOINT = { @@ -77,8 +56,8 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [value]\n\nData Flow Tree:\n└── value (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":6,"index":244},"end":{"line":9,"column":19,"index":257},"filename":"derived-state-conditionally-in-effect.ts","identifierName":"setLocalValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":16,"column":1,"index":378},"filename":"derived-state-conditionally-in-effect.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [value]\n\nData Flow Tree:\n└── value (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":6,"index":263},"end":{"line":9,"column":19,"index":276},"filename":"derived-state-conditionally-in-effect.ts","identifierName":"setLocalValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":16,"column":1,"index":397},"filename":"derived-state-conditionally-in-effect.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-conditionally-in-effect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-conditionally-in-effect.js index fb65cbfeb82b..4cdcb53bb236 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-conditionally-in-effect.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-conditionally-in-effect.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({value, enabled}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-default-props.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-default-props.expect.md index 2f3a3d0e6160..4db10f4df4cf 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-default-props.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-default-props.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; export default function Component({input = 'empty'}) { @@ -26,38 +26,18 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; -export default function Component(t0) { - const $ = _c(5); - const { input: t1 } = t0; - const input = t1 === undefined ? "empty" : t1; +export default function Component({ input = "empty" }) { const [currInput, setCurrInput] = useState(input); - let t2; - let t3; - if ($[0] !== input) { - t2 = () => { - setCurrInput(input + "local const"); - }; - t3 = [input, "local const"]; - $[0] = input; - $[1] = t2; - $[2] = t3; - } else { - t2 = $[1]; - t3 = $[2]; - } - useEffect(t2, t3); - let t4; - if ($[3] !== currInput) { - t4 =
{currInput}
; - $[3] = currInput; - $[4] = t4; - } else { - t4 = $[4]; - } - return t4; + const localConst = "local const"; + + useEffect(() => { + setCurrInput(input + localConst); + }, [input, localConst]); + + return
{currInput}
; } export const FIXTURE_ENTRYPOINT = { @@ -70,8 +50,8 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [input]\n\nData Flow Tree:\n└── input (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":4,"index":276},"end":{"line":9,"column":16,"index":288},"filename":"derived-state-from-default-props.ts","identifierName":"setCurrInput"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":122},"end":{"line":13,"column":1,"index":372},"filename":"derived-state-from-default-props.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [input]\n\nData Flow Tree:\n└── input (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":4,"index":295},"end":{"line":9,"column":16,"index":307},"filename":"derived-state-from-default-props.ts","identifierName":"setCurrInput"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":141},"end":{"line":13,"column":1,"index":391},"filename":"derived-state-from-default-props.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-default-props.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-default-props.js index 1de911c9b379..9d559946bead 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-default-props.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-default-props.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; export default function Component({input = 'empty'}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-local-state-in-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-local-state-in-effect.expect.md index 37458dcea06c..afddca39e9a7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-local-state-in-effect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-local-state-in-effect.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; @@ -23,45 +23,20 @@ function Component({shouldChange}) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; -function Component(t0) { - const $ = _c(7); - const { shouldChange } = t0; +function Component({ shouldChange }) { const [count, setCount] = useState(0); - let t1; - if ($[0] !== count || $[1] !== shouldChange) { - t1 = () => { - if (shouldChange) { - setCount(count + 1); - } - }; - $[0] = count; - $[1] = shouldChange; - $[2] = t1; - } else { - t1 = $[2]; - } - let t2; - if ($[3] !== count) { - t2 = [count]; - $[3] = count; - $[4] = t2; - } else { - t2 = $[4]; - } - useEffect(t1, t2); - let t3; - if ($[5] !== count) { - t3 =
{count}
; - $[5] = count; - $[6] = t3; - } else { - t3 = $[6]; - } - return t3; + + useEffect(() => { + if (shouldChange) { + setCount(count + 1); + } + }, [count]); + + return
{count}
; } ``` @@ -69,8 +44,8 @@ function Component(t0) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [count]\n\nData Flow Tree:\n└── count (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":10,"column":6,"index":237},"end":{"line":10,"column":14,"index":245},"filename":"derived-state-from-local-state-in-effect.ts","identifierName":"setCount"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":108},"end":{"line":15,"column":1,"index":310},"filename":"derived-state-from-local-state-in-effect.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [count]\n\nData Flow Tree:\n└── count (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":10,"column":6,"index":256},"end":{"line":10,"column":14,"index":264},"filename":"derived-state-from-local-state-in-effect.ts","identifierName":"setCount"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":127},"end":{"line":15,"column":1,"index":329},"filename":"derived-state-from-local-state-in-effect.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-local-state-in-effect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-local-state-in-effect.js index 79e65e4849a9..db84ab8be45f 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-local-state-in-effect.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-local-state-in-effect.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-local-state-and-component-scope.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-local-state-and-component-scope.expect.md index fdcbccd3de5b..e1c33a6c73f4 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-local-state-and-component-scope.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-local-state-and-component-scope.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({firstName}) { @@ -33,68 +33,25 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; -function Component(t0) { - const $ = _c(12); - const { firstName } = t0; +function Component({ firstName }) { const [lastName, setLastName] = useState("Doe"); const [fullName, setFullName] = useState("John"); - let t1; - let t2; - if ($[0] !== firstName || $[1] !== lastName) { - t1 = () => { - setFullName(firstName + " " + "D." + " " + lastName); - }; - t2 = [firstName, "D.", lastName]; - $[0] = firstName; - $[1] = lastName; - $[2] = t1; - $[3] = t2; - } else { - t1 = $[2]; - t2 = $[3]; - } - useEffect(t1, t2); - let t3; - if ($[4] === Symbol.for("react.memo_cache_sentinel")) { - t3 = (e) => setLastName(e.target.value); - $[4] = t3; - } else { - t3 = $[4]; - } - let t4; - if ($[5] !== lastName) { - t4 = ; - $[5] = lastName; - $[6] = t4; - } else { - t4 = $[6]; - } - let t5; - if ($[7] !== fullName) { - t5 =
{fullName}
; - $[7] = fullName; - $[8] = t5; - } else { - t5 = $[8]; - } - let t6; - if ($[9] !== t4 || $[10] !== t5) { - t6 = ( -
- {t4} - {t5} -
- ); - $[9] = t4; - $[10] = t5; - $[11] = t6; - } else { - t6 = $[11]; - } - return t6; + + const middleName = "D."; + + useEffect(() => { + setFullName(firstName + " " + middleName + " " + lastName); + }, [firstName, middleName, lastName]); + + return ( +
+ setLastName(e.target.value)} /> +
{fullName}
+
+ ); } export const FIXTURE_ENTRYPOINT = { @@ -107,8 +64,8 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [firstName]\nState: [lastName]\n\nData Flow Tree:\n├── firstName (Prop)\n└── lastName (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":11,"column":4,"index":297},"end":{"line":11,"column":15,"index":308},"filename":"derived-state-from-prop-local-state-and-component-scope.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":20,"column":1,"index":542},"filename":"derived-state-from-prop-local-state-and-component-scope.ts"},"fnName":"Component","memoSlots":12,"memoBlocks":5,"memoValues":6,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [firstName]\nState: [lastName]\n\nData Flow Tree:\n├── firstName (Prop)\n└── lastName (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":11,"column":4,"index":316},"end":{"line":11,"column":15,"index":327},"filename":"derived-state-from-prop-local-state-and-component-scope.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":20,"column":1,"index":561},"filename":"derived-state-from-prop-local-state-and-component-scope.ts"},"fnName":"Component","memoSlots":12,"memoBlocks":5,"memoValues":6,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-local-state-and-component-scope.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-local-state-and-component-scope.js index f25e20863d33..31b77d148171 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-local-state-and-component-scope.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-local-state-and-component-scope.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({firstName}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.expect.md index 698148254500..960cf987d4ee 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({initialName}) { @@ -29,48 +29,21 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; -function Component(t0) { - const $ = _c(6); - const { initialName } = t0; +function Component({ initialName }) { const [name, setName] = useState(""); - let t1; - let t2; - if ($[0] !== initialName) { - t1 = () => { - setName(initialName); - }; - t2 = [initialName]; - $[0] = initialName; - $[1] = t1; - $[2] = t2; - } else { - t1 = $[1]; - t2 = $[2]; - } - useEffect(t1, t2); - let t3; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { - t3 = (e) => setName(e.target.value); - $[3] = t3; - } else { - t3 = $[3]; - } - let t4; - if ($[4] !== name) { - t4 = ( -
- -
- ); - $[4] = name; - $[5] = t4; - } else { - t4 = $[5]; - } - return t4; + + useEffect(() => { + setName(initialName); + }, [initialName]); + + return ( +
+ setName(e.target.value)} /> +
+ ); } export const FIXTURE_ENTRYPOINT = { @@ -83,7 +56,7 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":16,"column":1,"index":359},"filename":"derived-state-from-prop-setter-call-outside-effect-no-error.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":3,"memoValues":4,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":16,"column":1,"index":378},"filename":"derived-state-from-prop-setter-call-outside-effect-no-error.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":3,"memoValues":4,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.js index 6df5f2eed6f9..e454caf2ec81 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-call-outside-effect-no-error.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({initialName}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-ternary.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-ternary.expect.md index 48811aa5a943..226a3938bd7d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-ternary.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-ternary.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp +// @validateNoDerivedComputationsInEffects_exp @outputMode:"lint" function Component({value}) { const [checked, setChecked] = useState(''); @@ -19,36 +19,16 @@ function Component({value}) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp +// @validateNoDerivedComputationsInEffects_exp @outputMode:"lint" -function Component(t0) { - const $ = _c(5); - const { value } = t0; +function Component({ value }) { const [checked, setChecked] = useState(""); - let t1; - let t2; - if ($[0] !== value) { - t1 = () => { - setChecked(value === "" ? [] : value.split(",")); - }; - t2 = [value]; - $[0] = value; - $[1] = t1; - $[2] = t2; - } else { - t1 = $[1]; - t2 = $[2]; - } - useEffect(t1, t2); - let t3; - if ($[3] !== checked) { - t3 =
{checked}
; - $[3] = checked; - $[4] = t3; - } else { - t3 = $[4]; - } - return t3; + + useEffect(() => { + setChecked(value === "" ? [] : value.split(",")); + }, [value]); + + return
{checked}
; } ``` diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-ternary.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-ternary.js index afd198caa262..1c020d3015d1 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-ternary.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-ternary.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp +// @validateNoDerivedComputationsInEffects_exp @outputMode:"lint" function Component({value}) { const [checked, setChecked] = useState(''); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.expect.md index b5100dc2a633..de184ede6394 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function MockComponent({onSet}) { @@ -28,50 +28,20 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; -function MockComponent(t0) { - const $ = _c(2); - const { onSet } = t0; - let t1; - if ($[0] !== onSet) { - t1 =
onSet("clicked")}>Mock Component
; - $[0] = onSet; - $[1] = t1; - } else { - t1 = $[1]; - } - return t1; +function MockComponent({ onSet }) { + return
onSet("clicked")}>Mock Component
; } -function Component(t0) { - const $ = _c(4); - const { propValue } = t0; - const [, setValue] = useState(null); - let t1; - let t2; - if ($[0] !== propValue) { - t1 = () => { - setValue(propValue); - }; - t2 = [propValue]; - $[0] = propValue; - $[1] = t1; - $[2] = t2; - } else { - t1 = $[1]; - t2 = $[2]; - } - useEffect(t1, t2); - let t3; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { - t3 = ; - $[3] = t3; - } else { - t3 = $[3]; - } - return t3; +function Component({ propValue }) { + const [value, setValue] = useState(null); + useEffect(() => { + setValue(propValue); + }, [propValue]); + + return ; } export const FIXTURE_ENTRYPOINT = { @@ -84,8 +54,8 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":6,"column":1,"index":211},"filename":"derived-state-from-prop-setter-used-outside-effect-no-error.ts"},"fnName":"MockComponent","memoSlots":2,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":8,"column":0,"index":213},"end":{"line":15,"column":1,"index":402},"filename":"derived-state-from-prop-setter-used-outside-effect-no-error.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":6,"column":1,"index":230},"filename":"derived-state-from-prop-setter-used-outside-effect-no-error.ts"},"fnName":"MockComponent","memoSlots":2,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":8,"column":0,"index":232},"end":{"line":15,"column":1,"index":421},"filename":"derived-state-from-prop-setter-used-outside-effect-no-error.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.js index 43b5a8c52ada..879d582c924d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-setter-used-outside-effect-no-error.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function MockComponent({onSet}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-with-side-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-with-side-effect.expect.md index 0160fbbb4a71..fc4d86a3f292 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-with-side-effect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-with-side-effect.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({value}) { @@ -26,38 +26,18 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; -function Component(t0) { - const $ = _c(5); - const { value } = t0; +function Component({ value }) { const [localValue, setLocalValue] = useState(""); - let t1; - let t2; - if ($[0] !== value) { - t1 = () => { - setLocalValue(value); - document.title = `Value: ${value}`; - }; - t2 = [value]; - $[0] = value; - $[1] = t1; - $[2] = t2; - } else { - t1 = $[1]; - t2 = $[2]; - } - useEffect(t1, t2); - let t3; - if ($[3] !== localValue) { - t3 =
{localValue}
; - $[3] = localValue; - $[4] = t3; - } else { - t3 = $[4]; - } - return t3; + + useEffect(() => { + setLocalValue(value); + document.title = `Value: ${value}`; + }, [value]); + + return
{localValue}
; } export const FIXTURE_ENTRYPOINT = { @@ -70,8 +50,8 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [value]\n\nData Flow Tree:\n└── value (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":8,"column":4,"index":214},"end":{"line":8,"column":17,"index":227},"filename":"derived-state-from-prop-with-side-effect.ts","identifierName":"setLocalValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":13,"column":1,"index":327},"filename":"derived-state-from-prop-with-side-effect.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [value]\n\nData Flow Tree:\n└── value (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":8,"column":4,"index":233},"end":{"line":8,"column":17,"index":246},"filename":"derived-state-from-prop-with-side-effect.ts","identifierName":"setLocalValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":13,"column":1,"index":346},"filename":"derived-state-from-prop-with-side-effect.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-with-side-effect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-with-side-effect.js index 5bb963daac32..b6cebdb40807 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-with-side-effect.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-prop-with-side-effect.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({value}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.expect.md index e88d5833a9a1..f11a4b26b533 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState, useRef} from 'react'; export default function Component({test}) { @@ -27,39 +27,19 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState, useRef } from "react"; -export default function Component(t0) { - const $ = _c(5); - const { test } = t0; +export default function Component({ test }) { const [local, setLocal] = useState(""); const myRef = useRef(null); - let t1; - let t2; - if ($[0] !== test) { - t1 = () => { - setLocal(myRef.current + test); - }; - t2 = [test]; - $[0] = test; - $[1] = t1; - $[2] = t2; - } else { - t1 = $[1]; - t2 = $[2]; - } - useEffect(t1, t2); - let t3; - if ($[3] !== local) { - t3 = <>{local}; - $[3] = local; - $[4] = t3; - } else { - t3 = $[4]; - } - return t3; + + useEffect(() => { + setLocal(myRef.current + test); + }, [test]); + + return <>{local}; } export const FIXTURE_ENTRYPOINT = { @@ -72,7 +52,7 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":130},"end":{"line":14,"column":1,"index":328},"filename":"derived-state-from-ref-and-state-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":149},"end":{"line":14,"column":1,"index":347},"filename":"derived-state-from-ref-and-state-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.js index 18ad2bdca19f..9425aee24419 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/derived-state-from-ref-and-state-no-error.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState, useRef} from 'react'; export default function Component({test}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-local-function-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-local-function-call.expect.md index fdc7081f3758..080aa8e04dcd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-local-function-call.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-local-function-call.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({propValue}) { @@ -30,48 +30,22 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; -function Component(t0) { - const $ = _c(6); - const { propValue } = t0; +function Component({ propValue }) { const [value, setValue] = useState(null); - let t1; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t1 = function localFunction() { - console.log("local function"); - }; - $[0] = t1; - } else { - t1 = $[0]; - } - const localFunction = t1; - let t2; - let t3; - if ($[1] !== propValue) { - t2 = () => { - setValue(propValue); - localFunction(); - }; - t3 = [propValue]; - $[1] = propValue; - $[2] = t2; - $[3] = t3; - } else { - t2 = $[2]; - t3 = $[3]; - } - useEffect(t2, t3); - let t4; - if ($[4] !== value) { - t4 =
{value}
; - $[4] = value; - $[5] = t4; - } else { - t4 = $[5]; + + function localFunction() { + console.log("local function"); } - return t4; + + useEffect(() => { + setValue(propValue); + localFunction(); + }, [propValue]); + + return
{value}
; } export const FIXTURE_ENTRYPOINT = { @@ -84,8 +58,8 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [propValue]\n\nData Flow Tree:\n└── propValue (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":12,"column":4,"index":279},"end":{"line":12,"column":12,"index":287},"filename":"effect-contains-local-function-call.ts","identifierName":"setValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":17,"column":1,"index":371},"filename":"effect-contains-local-function-call.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":3,"memoValues":4,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [propValue]\n\nData Flow Tree:\n└── propValue (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":12,"column":4,"index":298},"end":{"line":12,"column":12,"index":306},"filename":"effect-contains-local-function-call.ts","identifierName":"setValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":17,"column":1,"index":390},"filename":"effect-contains-local-function-call.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":3,"memoValues":4,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-local-function-call.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-local-function-call.js index a6442b36477f..3eabb40feeb6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-local-function-call.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-local-function-call.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({propValue}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.expect.md index 74391e86ad37..374877863dd3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({propValue, onChange}) { @@ -25,43 +25,17 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; -function Component(t0) { - const $ = _c(7); - const { propValue, onChange } = t0; +function Component({ propValue, onChange }) { const [value, setValue] = useState(null); - let t1; - if ($[0] !== onChange || $[1] !== propValue) { - t1 = () => { - setValue(propValue); - onChange(); - }; - $[0] = onChange; - $[1] = propValue; - $[2] = t1; - } else { - t1 = $[2]; - } - let t2; - if ($[3] !== propValue) { - t2 = [propValue]; - $[3] = propValue; - $[4] = t2; - } else { - t2 = $[4]; - } - useEffect(t1, t2); - let t3; - if ($[5] !== value) { - t3 =
{value}
; - $[5] = value; - $[6] = t3; - } else { - t3 = $[6]; - } - return t3; + useEffect(() => { + setValue(propValue); + onChange(); + }, [propValue]); + + return
{value}
; } export const FIXTURE_ENTRYPOINT = { @@ -74,8 +48,8 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":12,"column":1,"index":306},"filename":"effect-contains-prop-function-call-no-error.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":16,"column":41,"index":402},"end":{"line":16,"column":49,"index":410},"filename":"effect-contains-prop-function-call-no-error.ts"},"fnName":null,"memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":12,"column":1,"index":325},"filename":"effect-contains-prop-function-call-no-error.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":16,"column":41,"index":421},"end":{"line":16,"column":49,"index":429},"filename":"effect-contains-prop-function-call-no-error.ts"},"fnName":null,"memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.js index f19e9518d6fa..c9c9778ab844 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-contains-prop-function-call-no-error.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({propValue, onChange}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-used-in-dep-array-still-errors.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-used-in-dep-array-still-errors.expect.md index 6a3593a3b27c..1bd8fa23faa7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-used-in-dep-array-still-errors.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-used-in-dep-array-still-errors.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" function Component({prop}) { const [s, setS] = useState(0); @@ -18,36 +18,15 @@ function Component({prop}) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly - -function Component(t0) { - const $ = _c(5); - const { prop } = t0; - const [, setS] = useState(0); - let t1; - let t2; - if ($[0] !== prop) { - t1 = () => { - setS(prop); - }; - t2 = [prop, setS]; - $[0] = prop; - $[1] = t1; - $[2] = t2; - } else { - t1 = $[1]; - t2 = $[2]; - } - useEffect(t1, t2); - let t3; - if ($[3] !== prop) { - t3 =
{prop}
; - $[3] = prop; - $[4] = t3; - } else { - t3 = $[4]; - } - return t3; +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" + +function Component({ prop }) { + const [s, setS] = useState(0); + useEffect(() => { + setS(prop); + }, [prop, setS]); + + return
{prop}
; } ``` @@ -55,8 +34,8 @@ function Component(t0) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [prop]\n\nData Flow Tree:\n└── prop (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":6,"column":4,"index":150},"end":{"line":6,"column":8,"index":154},"filename":"effect-used-in-dep-array-still-errors.ts","identifierName":"setS"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":64},"end":{"line":10,"column":1,"index":212},"filename":"effect-used-in-dep-array-still-errors.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [prop]\n\nData Flow Tree:\n└── prop (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":6,"column":4,"index":169},"end":{"line":6,"column":8,"index":173},"filename":"effect-used-in-dep-array-still-errors.ts","identifierName":"setS"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":83},"end":{"line":10,"column":1,"index":231},"filename":"effect-used-in-dep-array-still-errors.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-used-in-dep-array-still-errors.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-used-in-dep-array-still-errors.js index 1df99a191dcf..bf48efbbc100 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-used-in-dep-array-still-errors.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-used-in-dep-array-still-errors.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" function Component({prop}) { const [s, setS] = useState(0); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-cleanup-function-depending-on-derived-computation-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-cleanup-function-depending-on-derived-computation-value.expect.md index e84031591e07..ac72a4414844 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-cleanup-function-depending-on-derived-computation-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-cleanup-function-depending-on-derived-computation-value.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; @@ -29,39 +29,26 @@ function Component(file: File) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; -function Component(file) { - const $ = _c(5); +function Component(file: File) { const [imageUrl, setImageUrl] = useState(null); - let t0; - let t1; - if ($[0] !== file) { - t0 = () => { - const imageUrlPrepared = URL.createObjectURL(file); - setImageUrl(imageUrlPrepared); - return () => URL.revokeObjectURL(imageUrlPrepared); - }; - t1 = [file]; - $[0] = file; - $[1] = t0; - $[2] = t1; - } else { - t0 = $[1]; - t1 = $[2]; - } - useEffect(t0, t1); - let t2; - if ($[3] !== imageUrl) { - t2 = ; - $[3] = imageUrl; - $[4] = t2; - } else { - t2 = $[4]; - } - return t2; + + /* + * Cleaning up the variable or a source of the variable used to setState + * inside the effect communicates that we always need to clean up something + * which is a valid use case for useEffect. In which case we want to + * avoid an throwing + */ + useEffect(() => { + const imageUrlPrepared = URL.createObjectURL(file); + setImageUrl(imageUrlPrepared); + return () => URL.revokeObjectURL(imageUrlPrepared); + }, [file]); + + return ; } ``` @@ -69,7 +56,7 @@ function Component(file) { ## Logs ``` -{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":108},"end":{"line":21,"column":1,"index":700},"filename":"effect-with-cleanup-function-depending-on-derived-computation-value.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":127},"end":{"line":21,"column":1,"index":719},"filename":"effect-with-cleanup-function-depending-on-derived-computation-value.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-cleanup-function-depending-on-derived-computation-value.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-cleanup-function-depending-on-derived-computation-value.js index e419583cc6dd..16e52562bd83 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-cleanup-function-depending-on-derived-computation-value.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-cleanup-function-depending-on-derived-computation-value.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.expect.md index e26643723d98..58328b2e9597 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({propValue}) { @@ -25,38 +25,17 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; -function Component(t0) { - const $ = _c(5); - const { propValue } = t0; +function Component({ propValue }) { const [value, setValue] = useState(null); - let t1; - let t2; - if ($[0] !== propValue) { - t1 = () => { - setValue(propValue); - globalCall(); - }; - t2 = [propValue]; - $[0] = propValue; - $[1] = t1; - $[2] = t2; - } else { - t1 = $[1]; - t2 = $[2]; - } - useEffect(t1, t2); - let t3; - if ($[3] !== value) { - t3 =
{value}
; - $[3] = value; - $[4] = t3; - } else { - t3 = $[4]; - } - return t3; + useEffect(() => { + setValue(propValue); + globalCall(); + }, [propValue]); + + return
{value}
; } export const FIXTURE_ENTRYPOINT = { @@ -69,7 +48,7 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":12,"column":1,"index":298},"filename":"effect-with-global-function-call-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":12,"column":1,"index":317},"filename":"effect-with-global-function-call-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.js index ae7622d4d064..565e23bb0fe3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/effect-with-global-function-call-no-error.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component({propValue}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/from-props-setstate-in-effect-no-error.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/from-props-setstate-in-effect-no-error.expect.md index f23f51d6cb8b..7ba22f518642 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/from-props-setstate-in-effect-no-error.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/from-props-setstate-in-effect-no-error.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @enableTreatSetIdentifiersAsStateSetters @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @enableTreatSetIdentifiersAsStateSetters @loggerTestOnly @outputMode:"lint" function Component({setParentState, prop}) { useEffect(() => { @@ -17,40 +17,14 @@ function Component({setParentState, prop}) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @enableTreatSetIdentifiersAsStateSetters @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @enableTreatSetIdentifiersAsStateSetters @loggerTestOnly @outputMode:"lint" -function Component(t0) { - const $ = _c(7); - const { setParentState, prop } = t0; - let t1; - if ($[0] !== prop || $[1] !== setParentState) { - t1 = () => { - setParentState(prop); - }; - $[0] = prop; - $[1] = setParentState; - $[2] = t1; - } else { - t1 = $[2]; - } - let t2; - if ($[3] !== prop) { - t2 = [prop]; - $[3] = prop; - $[4] = t2; - } else { - t2 = $[4]; - } - useEffect(t1, t2); - let t3; - if ($[5] !== prop) { - t3 =
{prop}
; - $[5] = prop; - $[6] = t3; - } else { - t3 = $[6]; - } - return t3; +function Component({ setParentState, prop }) { + useEffect(() => { + setParentState(prop); + }, [prop]); + + return
{prop}
; } ``` @@ -58,7 +32,7 @@ function Component(t0) { ## Logs ``` -{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":105},"end":{"line":9,"column":1,"index":240},"filename":"from-props-setstate-in-effect-no-error.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":124},"end":{"line":9,"column":1,"index":259},"filename":"from-props-setstate-in-effect-no-error.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/from-props-setstate-in-effect-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/from-props-setstate-in-effect-no-error.js index 4075975b3254..1754209d8371 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/from-props-setstate-in-effect-no-error.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/from-props-setstate-in-effect-no-error.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @enableTreatSetIdentifiersAsStateSetters @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @enableTreatSetIdentifiersAsStateSetters @loggerTestOnly @outputMode:"lint" function Component({setParentState, prop}) { useEffect(() => { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/function-expression-mutation-edge-case.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/function-expression-mutation-edge-case.expect.md index 865ec9487eb3..c1b99a95ab89 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/function-expression-mutation-edge-case.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/function-expression-mutation-edge-case.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" function Component() { const [foo, setFoo] = useState({}); @@ -40,63 +40,37 @@ function Component() { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" function Component() { - const $ = _c(9); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = {}; - $[0] = t0; - } else { - t0 = $[0]; - } - const [foo, setFoo] = useState(t0); - let t1; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = new Set(); - $[1] = t1; - } else { - t1 = $[1]; - } - const [bar] = useState(t1); - let t2; - let t3; - if ($[2] !== bar || $[3] !== foo) { - t2 = () => { - let isChanged = false; - const newData = foo.map((val) => { - bar.someMethod(val); - isChanged = true; - }); - if (isChanged) { - setFoo(newData); - } - }; - t3 = [foo, bar]; - $[2] = bar; - $[3] = foo; - $[4] = t2; - $[5] = t3; - } else { - t2 = $[4]; - t3 = $[5]; - } - useEffect(t2, t3); - let t4; - if ($[6] !== bar || $[7] !== foo) { - t4 = ( -
- {foo}, {bar} -
- ); - $[6] = bar; - $[7] = foo; - $[8] = t4; - } else { - t4 = $[8]; - } - return t4; + const [foo, setFoo] = useState({}); + const [bar, setBar] = useState(new Set()); + + /* + * isChanged is considered context of the effect's function expression, + * if we don't bail out of effect mutation derivation tracking, isChanged + * will inherit the sources of the effect's function expression. + * + * This is innacurate and with the multiple passes ends up causing an infinite loop. + */ + useEffect(() => { + let isChanged = false; + + const newData = foo.map((val) => { + bar.someMethod(val); + isChanged = true; + }); + + if (isChanged) { + setFoo(newData); + } + }, [foo, bar]); + + return ( +
+ {foo}, {bar} +
+ ); } ``` @@ -104,8 +78,8 @@ function Component() { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [foo, bar]\n\nData Flow Tree:\n└── newData\n ├── foo (State)\n └── bar (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":23,"column":6,"index":663},"end":{"line":23,"column":12,"index":669},"filename":"function-expression-mutation-edge-case.ts","identifierName":"setFoo"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":64},"end":{"line":32,"column":1,"index":762},"filename":"function-expression-mutation-edge-case.ts"},"fnName":"Component","memoSlots":9,"memoBlocks":4,"memoValues":5,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [foo, bar]\n\nData Flow Tree:\n└── newData\n ├── foo (State)\n └── bar (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":23,"column":6,"index":682},"end":{"line":23,"column":12,"index":688},"filename":"function-expression-mutation-edge-case.ts","identifierName":"setFoo"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":83},"end":{"line":32,"column":1,"index":781},"filename":"function-expression-mutation-edge-case.ts"},"fnName":"Component","memoSlots":9,"memoBlocks":4,"memoValues":5,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/function-expression-mutation-edge-case.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/function-expression-mutation-edge-case.js index ab0bd70f363f..856209928d1c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/function-expression-mutation-edge-case.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/function-expression-mutation-edge-case.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" function Component() { const [foo, setFoo] = useState({}); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-computation-in-effect.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-computation-in-effect.expect.md index 29dea440b463..928b7e9f7129 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-computation-in-effect.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-computation-in-effect.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component() { @@ -28,38 +28,20 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; function Component() { - const $ = _c(5); - const [firstName] = useState("Taylor"); + const [firstName, setFirstName] = useState("Taylor"); + const lastName = "Swift"; + // 🔴 Avoid: redundant state and unnecessary Effect const [fullName, setFullName] = useState(""); - let t0; - let t1; - if ($[0] !== firstName) { - t0 = () => { - setFullName(firstName + " " + "Swift"); - }; - t1 = [firstName, "Swift"]; - $[0] = firstName; - $[1] = t0; - $[2] = t1; - } else { - t0 = $[1]; - t1 = $[2]; - } - useEffect(t0, t1); - let t2; - if ($[3] !== fullName) { - t2 =
{fullName}
; - $[3] = fullName; - $[4] = t2; - } else { - t2 = $[4]; - } - return t2; + useEffect(() => { + setFullName(firstName + " " + lastName); + }, [firstName, lastName]); + + return
{fullName}
; } export const FIXTURE_ENTRYPOINT = { @@ -72,8 +54,8 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [firstName]\n\nData Flow Tree:\n└── firstName (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":11,"column":4,"index":341},"end":{"line":11,"column":15,"index":352},"filename":"invalid-derived-computation-in-effect.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":107},"end":{"line":15,"column":1,"index":445},"filename":"invalid-derived-computation-in-effect.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [firstName]\n\nData Flow Tree:\n└── firstName (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":11,"column":4,"index":360},"end":{"line":11,"column":15,"index":371},"filename":"invalid-derived-computation-in-effect.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":126},"end":{"line":15,"column":1,"index":464},"filename":"invalid-derived-computation-in-effect.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-computation-in-effect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-computation-in-effect.js index e29ece67bd57..6cd45831215a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-computation-in-effect.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-computation-in-effect.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-computed-props.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-computed-props.expect.md index c7199d95480f..c627b583b25b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-computed-props.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-computed-props.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; export default function Component(props) { @@ -26,39 +26,18 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; export default function Component(props) { - const $ = _c(7); const [displayValue, setDisplayValue] = useState(""); - let t0; - let t1; - if ($[0] !== props.prefix || $[1] !== props.suffix || $[2] !== props.value) { - t0 = () => { - const computed = props.prefix + props.value + props.suffix; - setDisplayValue(computed); - }; - t1 = [props.prefix, props.value, props.suffix]; - $[0] = props.prefix; - $[1] = props.suffix; - $[2] = props.value; - $[3] = t0; - $[4] = t1; - } else { - t0 = $[3]; - t1 = $[4]; - } - useEffect(t0, t1); - let t2; - if ($[5] !== displayValue) { - t2 =
{displayValue}
; - $[5] = displayValue; - $[6] = t2; - } else { - t2 = $[6]; - } - return t2; + + useEffect(() => { + const computed = props.prefix + props.value + props.suffix; + setDisplayValue(computed); + }, [props.prefix, props.value, props.suffix]); + + return
{displayValue}
; } export const FIXTURE_ENTRYPOINT = { @@ -71,8 +50,8 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [props]\n\nData Flow Tree:\n└── computed\n └── props (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":4,"index":295},"end":{"line":9,"column":19,"index":310},"filename":"invalid-derived-state-from-computed-props.ts","identifierName":"setDisplayValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":122},"end":{"line":13,"column":1,"index":409},"filename":"invalid-derived-state-from-computed-props.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [props]\n\nData Flow Tree:\n└── computed\n └── props (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":9,"column":4,"index":314},"end":{"line":9,"column":19,"index":329},"filename":"invalid-derived-state-from-computed-props.ts","identifierName":"setDisplayValue"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":141},"end":{"line":13,"column":1,"index":428},"filename":"invalid-derived-state-from-computed-props.ts"},"fnName":"Component","memoSlots":7,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-computed-props.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-computed-props.js index 39648ef8d5ee..4243834c2f6c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-computed-props.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-computed-props.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; export default function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-destructured-props.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-destructured-props.expect.md index 5a6af555408e..858daba50230 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-destructured-props.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-destructured-props.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; export default function Component({props}) { @@ -27,40 +27,19 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState } from "react"; -export default function Component(t0) { - const $ = _c(6); - const { props } = t0; +export default function Component({ props }) { const [fullName, setFullName] = useState( props.firstName + " " + props.lastName, ); - let t1; - let t2; - if ($[0] !== props.firstName || $[1] !== props.lastName) { - t1 = () => { - setFullName(props.firstName + " " + props.lastName); - }; - t2 = [props.firstName, props.lastName]; - $[0] = props.firstName; - $[1] = props.lastName; - $[2] = t1; - $[3] = t2; - } else { - t1 = $[2]; - t2 = $[3]; - } - useEffect(t1, t2); - let t3; - if ($[4] !== fullName) { - t3 =
{fullName}
; - $[4] = fullName; - $[5] = t3; - } else { - t3 = $[5]; - } - return t3; + + useEffect(() => { + setFullName(props.firstName + " " + props.lastName); + }, [props.firstName, props.lastName]); + + return
{fullName}
; } export const FIXTURE_ENTRYPOINT = { @@ -73,8 +52,8 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [props]\n\nData Flow Tree:\n└── props (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":10,"column":4,"index":269},"end":{"line":10,"column":15,"index":280},"filename":"invalid-derived-state-from-destructured-props.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":122},"end":{"line":14,"column":1,"index":397},"filename":"invalid-derived-state-from-destructured-props.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nProps: [props]\n\nData Flow Tree:\n└── props (Prop)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":10,"column":4,"index":288},"end":{"line":10,"column":15,"index":299},"filename":"invalid-derived-state-from-destructured-props.ts","identifierName":"setFullName"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":141},"end":{"line":14,"column":1,"index":416},"filename":"invalid-derived-state-from-destructured-props.ts"},"fnName":"Component","memoSlots":6,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-destructured-props.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-destructured-props.js index 3f662f13f71c..abb1643e6921 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-destructured-props.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/invalid-derived-state-from-destructured-props.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState} from 'react'; export default function Component({props}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.expect.md index 7948a49b3a89..70174794dc78 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState, useRef} from 'react'; export default function Component({test}) { @@ -31,43 +31,23 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import { useEffect, useState, useRef } from "react"; -export default function Component(t0) { - const $ = _c(5); - const { test } = t0; +export default function Component({ test }) { const [local, setLocal] = useState(0); const myRef = useRef(null); - let t1; - let t2; - if ($[0] !== test) { - t1 = () => { - if (myRef.current) { - setLocal(test); - } else { - setLocal(test + test); - } - }; - t2 = [test]; - $[0] = test; - $[1] = t1; - $[2] = t2; - } else { - t1 = $[1]; - t2 = $[2]; - } - useEffect(t1, t2); - let t3; - if ($[3] !== local) { - t3 = <>{local}; - $[3] = local; - $[4] = t3; - } else { - t3 = $[4]; - } - return t3; + + useEffect(() => { + if (myRef.current) { + setLocal(test); + } else { + setLocal(test + test); + } + }, [test]); + + return <>{local}; } export const FIXTURE_ENTRYPOINT = { @@ -80,7 +60,7 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":130},"end":{"line":18,"column":1,"index":386},"filename":"ref-conditional-in-effect-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":15,"index":149},"end":{"line":18,"column":1,"index":405},"filename":"ref-conditional-in-effect-no-error.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.js index 3594deaa02e9..a5424ab03b2d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/ref-conditional-in-effect-no-error.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" import {useEffect, useState, useRef} from 'react'; export default function Component({test}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/usestate-derived-from-prop-no-show-in-data-flow-tree.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/usestate-derived-from-prop-no-show-in-data-flow-tree.expect.md index 87cf7722da35..690574e4429b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/usestate-derived-from-prop-no-show-in-data-flow-tree.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/usestate-derived-from-prop-no-show-in-data-flow-tree.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" function Component({prop}) { const [s, setS] = useState(); @@ -26,37 +26,23 @@ function Component({prop}) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" -function Component(t0) { - const $ = _c(5); - const { prop } = t0; +function Component({ prop }) { const [s, setS] = useState(); - const [second] = useState(prop); - let t1; - let t2; - if ($[0] !== second) { - t1 = () => { - setS(second); - }; - t2 = [second]; - $[0] = second; - $[1] = t1; - $[2] = t2; - } else { - t1 = $[1]; - t2 = $[2]; - } - useEffect(t1, t2); - let t3; - if ($[3] !== s) { - t3 =
{s}
; - $[3] = s; - $[4] = t3; - } else { - t3 = $[4]; - } - return t3; + const [second, setSecond] = useState(prop); + + /* + * `second` is a source of state. It will inherit the value of `prop` in + * the first render, but after that it will no longer be updated when + * `prop` changes. So we shouldn't consider `second` as being derived from + * `prop` + */ + useEffect(() => { + setS(second); + }, [second]); + + return
{s}
; } ``` @@ -64,8 +50,8 @@ function Component(t0) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [second]\n\nData Flow Tree:\n└── second (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":14,"column":4,"index":443},"end":{"line":14,"column":8,"index":447},"filename":"usestate-derived-from-prop-no-show-in-data-flow-tree.ts","identifierName":"setS"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":64},"end":{"line":18,"column":1,"index":500},"filename":"usestate-derived-from-prop-no-show-in-data-flow-tree.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"description":"Using an effect triggers an additional render which can hurt performance and user experience, potentially briefly showing stale values to the user\n\nThis setState call is setting a derived value that depends on the following reactive sources:\n\nState: [second]\n\nData Flow Tree:\n└── second (State)\n\nSee: https://react.dev/learn/you-might-not-need-an-effect#updating-state-based-on-props-or-state","category":"EffectDerivationsOfState","reason":"You might not need an effect. Derive values in render, not effects.","details":[{"kind":"error","loc":{"start":{"line":14,"column":4,"index":462},"end":{"line":14,"column":8,"index":466},"filename":"usestate-derived-from-prop-no-show-in-data-flow-tree.ts","identifierName":"setS"},"message":"This should be computed during render, not in an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":3,"column":0,"index":83},"end":{"line":18,"column":1,"index":519},"filename":"usestate-derived-from-prop-no-show-in-data-flow-tree.ts"},"fnName":"Component","memoSlots":5,"memoBlocks":2,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/usestate-derived-from-prop-no-show-in-data-flow-tree.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/usestate-derived-from-prop-no-show-in-data-flow-tree.js index 5a7a693d50b0..3be4e88a07ad 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/usestate-derived-from-prop-no-show-in-data-flow-tree.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/effect-derived-computations/usestate-derived-from-prop-no-show-in-data-flow-tree.js @@ -1,4 +1,4 @@ -// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly +// @validateNoDerivedComputationsInEffects_exp @loggerTestOnly @outputMode:"lint" function Component({prop}) { const [s, setS] = useState(); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.expect.md index d82575b8c354..b5c079f5423b 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoJSXInTryStatements +// @validateNoJSXInTryStatements @outputMode:"lint" import {identity} from 'shared-runtime'; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.js index 9db091a2fb7e..fbc0d292ce65 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-catch-in-outer-try-with-finally.js @@ -1,4 +1,4 @@ -// @validateNoJSXInTryStatements +// @validateNoJSXInTryStatements @outputMode:"lint" import {identity} from 'shared-runtime'; function Component(props) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-try-with-finally.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-try-with-finally.expect.md index e8a29205647e..79ae59e64c11 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-try-with-finally.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-try-with-finally.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoJSXInTryStatements +// @validateNoJSXInTryStatements @outputMode:"lint" function Component(props) { let el; try { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-try-with-finally.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-try-with-finally.js index f0a17391c0ee..da1682772144 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-try-with-finally.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-invalid-jsx-in-try-with-finally.js @@ -1,4 +1,4 @@ -// @validateNoJSXInTryStatements +// @validateNoJSXInTryStatements @outputMode:"lint" function Component(props) { let el; try { 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 5eaa1fd5040b..6ac06c1df23c 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 @@ -2,7 +2,7 @@ ## Input ```javascript -// @loggerTestOnly @validateNoJSXInTryStatements +// @loggerTestOnly @validateNoJSXInTryStatements @outputMode:"lint" import {identity} from 'shared-runtime'; function Component(props) { @@ -25,34 +25,17 @@ function Component(props) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateNoJSXInTryStatements +// @loggerTestOnly @validateNoJSXInTryStatements @outputMode:"lint" import { identity } from "shared-runtime"; function Component(props) { - const $ = _c(4); let el; try { let value; try { - let t0; - if ($[0] !== props.foo) { - t0 = identity(props.foo); - $[0] = props.foo; - $[1] = t0; - } else { - t0 = $[1]; - } - value = t0; + value = identity(props.foo); } catch { - let t0; - if ($[2] !== value) { - t0 =
; - $[2] = value; - $[3] = t0; - } else { - t0 = $[3]; - } - el = t0; + el =
; } } catch { return null; @@ -65,8 +48,8 @@ function Component(props) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"category":"ErrorBoundaries","reason":"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} +{"kind":"CompileError","detail":{"options":{"category":"ErrorBoundaries","reason":"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":241},"end":{"line":11,"column":32,"index":262},"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":110},"end":{"line":17,"column":1,"index":317},"filename":"invalid-jsx-in-catch-in-outer-try-with-catch.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.js index 3cf5cc9b8a60..a036272cdb05 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-catch-in-outer-try-with-catch.js @@ -1,4 +1,4 @@ -// @loggerTestOnly @validateNoJSXInTryStatements +// @loggerTestOnly @validateNoJSXInTryStatements @outputMode:"lint" import {identity} from 'shared-runtime'; function Component(props) { 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 323aedd869de..1e08cb24a663 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 @@ -2,7 +2,7 @@ ## Input ```javascript -// @loggerTestOnly @validateNoJSXInTryStatements +// @loggerTestOnly @validateNoJSXInTryStatements @outputMode:"lint" function Component(props) { let el; try { @@ -18,19 +18,11 @@ function Component(props) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateNoJSXInTryStatements +// @loggerTestOnly @validateNoJSXInTryStatements @outputMode:"lint" function Component(props) { - const $ = _c(1); let el; try { - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 =
; - $[0] = t0; - } else { - t0 = $[0]; - } - el = t0; + el =
; } catch { return null; } @@ -42,8 +34,8 @@ function Component(props) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"category":"ErrorBoundaries","reason":"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} +{"kind":"CompileError","detail":{"options":{"category":"ErrorBoundaries","reason":"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":123},"end":{"line":5,"column":16,"index":130},"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":68},"end":{"line":10,"column":1,"index":179},"filename":"invalid-jsx-in-try-with-catch.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.js index d01a93bf5afd..45d932ec7c42 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-jsx-in-try-with-catch.js @@ -1,4 +1,4 @@ -// @loggerTestOnly @validateNoJSXInTryStatements +// @loggerTestOnly @validateNoJSXInTryStatements @outputMode:"lint" function Component(props) { let el; try { 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 a327d0b8bd6a..5cd44a9c851e 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 @@ -2,7 +2,7 @@ ## Input ```javascript -// @loggerTestOnly @validateNoSetStateInEffects +// @loggerTestOnly @validateNoSetStateInEffects @outputMode:"lint" import {useEffect, useState} from 'react'; function Component() { @@ -24,48 +24,30 @@ function Component() { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateNoSetStateInEffects +// @loggerTestOnly @validateNoSetStateInEffects @outputMode:"lint" import { useEffect, useState } from "react"; function Component() { - const $ = _c(2); const [state, setState] = useState(0); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - const f = () => { - setState(_temp); - }; - t0 = () => { - f(); - }; - $[0] = t0; - } else { - t0 = $[0]; - } - const g = t0; - let t1; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = () => { - g(); - }; - $[1] = t1; - } else { - t1 = $[1]; - } - useEffect(t1); + const f = () => { + setState((s) => s + 1); + }; + const g = () => { + f(); + }; + useEffect(() => { + g(); + }); return state; } -function _temp(s) { - return s + 1; -} ``` ## Logs ``` -{"kind":"CompileError","detail":{"options":{"category":"EffectSetState","reason":"Calling setState synchronously within an effect can trigger cascading renders","description":"Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect)","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() directly within 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} +{"kind":"CompileError","detail":{"options":{"category":"EffectSetState","reason":"Calling setState synchronously within an effect can trigger cascading renders","description":"Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect)","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":13,"column":4,"index":284},"end":{"line":13,"column":5,"index":285},"filename":"invalid-setState-in-useEffect-transitive.ts","identifierName":"g"},"message":"Avoid calling setState() directly within an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":111},"end":{"line":16,"column":1,"index":312},"filename":"invalid-setState-in-useEffect-transitive.ts"},"fnName":"Component","memoSlots":2,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.js index 50007c0bd13a..ef69e4be4339 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-transitive.js @@ -1,4 +1,4 @@ -// @loggerTestOnly @validateNoSetStateInEffects +// @loggerTestOnly @validateNoSetStateInEffects @outputMode:"lint" import {useEffect, useState} from 'react'; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-via-useEffectEvent.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-via-useEffectEvent.expect.md index 946169205f0a..144cb7a52289 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-via-useEffectEvent.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-via-useEffectEvent.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @loggerTestOnly @validateNoSetStateInEffects +// @loggerTestOnly @validateNoSetStateInEffects @outputMode:"lint" import {useEffect, useEffectEvent, useState} from 'react'; function Component() { @@ -21,40 +21,17 @@ function Component() { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateNoSetStateInEffects +// @loggerTestOnly @validateNoSetStateInEffects @outputMode:"lint" import { useEffect, useEffectEvent, useState } from "react"; function Component() { - const $ = _c(4); const [state, setState] = useState(0); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - setState(true); - }; - $[0] = t0; - } else { - t0 = $[0]; - } - const effectEvent = useEffectEvent(t0); - let t1; - if ($[1] !== effectEvent) { - t1 = () => { - effectEvent(); - }; - $[1] = effectEvent; - $[2] = t1; - } else { - t1 = $[2]; - } - let t2; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { - t2 = []; - $[3] = t2; - } else { - t2 = $[3]; - } - useEffect(t1, t2); + const effectEvent = useEffectEvent(() => { + setState(true); + }); + useEffect(() => { + effectEvent(); + }, []); return state; } @@ -63,8 +40,8 @@ function Component() { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"category":"EffectSetState","reason":"Calling setState synchronously within an effect can trigger cascading renders","description":"Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect)","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":10,"column":4,"index":267},"end":{"line":10,"column":15,"index":278},"filename":"invalid-setState-in-useEffect-via-useEffectEvent.ts","identifierName":"effectEvent"},"message":"Avoid calling setState() directly within an effect"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":108},"end":{"line":13,"column":1,"index":309},"filename":"invalid-setState-in-useEffect-via-useEffectEvent.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"category":"EffectSetState","reason":"Calling setState synchronously within an effect can trigger cascading renders","description":"Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect)","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":10,"column":4,"index":286},"end":{"line":10,"column":15,"index":297},"filename":"invalid-setState-in-useEffect-via-useEffectEvent.ts","identifierName":"effectEvent"},"message":"Avoid calling setState() directly within an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":127},"end":{"line":13,"column":1,"index":328},"filename":"invalid-setState-in-useEffect-via-useEffectEvent.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":3,"memoValues":3,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-via-useEffectEvent.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-via-useEffectEvent.js index a838e58564ec..823ace42c347 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-via-useEffectEvent.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect-via-useEffectEvent.js @@ -1,4 +1,4 @@ -// @loggerTestOnly @validateNoSetStateInEffects +// @loggerTestOnly @validateNoSetStateInEffects @outputMode:"lint" import {useEffect, useEffectEvent, useState} from 'react'; function Component() { 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 3c3a5a8053ad..5022b5517188 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 @@ -2,7 +2,7 @@ ## Input ```javascript -// @loggerTestOnly @validateNoSetStateInEffects +// @loggerTestOnly @validateNoSetStateInEffects @outputMode:"lint" import {useEffect, useState} from 'react'; function Component() { @@ -18,35 +18,24 @@ function Component() { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateNoSetStateInEffects +// @loggerTestOnly @validateNoSetStateInEffects @outputMode:"lint" import { useEffect, useState } from "react"; function Component() { - const $ = _c(1); const [state, setState] = useState(0); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - setState(_temp); - }; - $[0] = t0; - } else { - t0 = $[0]; - } - useEffect(t0); + useEffect(() => { + setState((s) => s + 1); + }); return state; } -function _temp(s) { - return s + 1; -} ``` ## Logs ``` -{"kind":"CompileError","detail":{"options":{"category":"EffectSetState","reason":"Calling setState synchronously within an effect can trigger cascading renders","description":"Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect)","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() directly within 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} +{"kind":"CompileError","detail":{"options":{"category":"EffectSetState","reason":"Calling setState synchronously within an effect can trigger cascading renders","description":"Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:\n* Update external systems with the latest state from React.\n* Subscribe for updates from some external system, calling setState in a callback function when external state changes.\n\nCalling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect)","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":7,"column":4,"index":199},"end":{"line":7,"column":12,"index":207},"filename":"invalid-setState-in-useEffect.ts","identifierName":"setState"},"message":"Avoid calling setState() directly within an effect"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":111},"end":{"line":10,"column":1,"index":244},"filename":"invalid-setState-in-useEffect.ts"},"fnName":"Component","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.js index a95d3642cb8d..d2422caea022 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/invalid-setState-in-useEffect.js @@ -1,4 +1,4 @@ -// @loggerTestOnly @validateNoSetStateInEffects +// @loggerTestOnly @validateNoSetStateInEffects @outputMode:"lint" import {useEffect, useState} from 'react'; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.expect.md index b6895fc396d9..6625f0153e8a 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { let Component; if (props.cond) { @@ -18,31 +18,15 @@ function Example(props) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { - const $ = _c(3); let Component; if (props.cond) { - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = createComponent(); - $[0] = t0; - } else { - t0 = $[0]; - } - Component = t0; + Component = createComponent(); } else { Component = DefaultComponent; } - let t0; - if ($[1] !== Component) { - t0 = ; - $[1] = Component; - $[2] = t0; - } else { - t0 = $[2]; - } - return t0; + return ; } ``` @@ -50,8 +34,8 @@ function Example(props) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render","details":[{"kind":"error","loc":{"start":{"line":9,"column":10,"index":202},"end":{"line":9,"column":19,"index":211},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":5,"column":16,"index":124},"end":{"line":5,"column":33,"index":141},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"},"message":"The component is created during render here"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":45},"end":{"line":10,"column":1,"index":217},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"},"fnName":"Example","memoSlots":3,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render","details":[{"kind":"error","loc":{"start":{"line":9,"column":10,"index":221},"end":{"line":9,"column":19,"index":230},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":5,"column":16,"index":143},"end":{"line":5,"column":33,"index":160},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"},"message":"The component is created during render here"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":64},"end":{"line":10,"column":1,"index":236},"filename":"invalid-conditionally-assigned-dynamically-constructed-component-in-render.ts"},"fnName":"Example","memoSlots":3,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.js index 6ff7ff321c33..1022cc9d5752 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-conditionally-assigned-dynamically-constructed-component-in-render.js @@ -1,4 +1,4 @@ -// @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { let Component; if (props.cond) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.expect.md index 3d9f1f76f96c..c6441bc4cb1e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { const Component = createComponent(); return ; @@ -13,18 +13,10 @@ function Example(props) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { - const $ = _c(1); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - const Component = createComponent(); - t0 = ; - $[0] = t0; - } else { - t0 = $[0]; - } - return t0; + const Component = createComponent(); + return ; } ``` @@ -32,8 +24,8 @@ function Example(props) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render","details":[{"kind":"error","loc":{"start":{"line":4,"column":10,"index":120},"end":{"line":4,"column":19,"index":129},"filename":"invalid-dynamically-construct-component-in-render.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":20,"index":91},"end":{"line":3,"column":37,"index":108},"filename":"invalid-dynamically-construct-component-in-render.ts"},"message":"The component is created during render here"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":45},"end":{"line":5,"column":1,"index":135},"filename":"invalid-dynamically-construct-component-in-render.ts"},"fnName":"Example","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render","details":[{"kind":"error","loc":{"start":{"line":4,"column":10,"index":139},"end":{"line":4,"column":19,"index":148},"filename":"invalid-dynamically-construct-component-in-render.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":20,"index":110},"end":{"line":3,"column":37,"index":127},"filename":"invalid-dynamically-construct-component-in-render.ts"},"message":"The component is created during render here"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":64},"end":{"line":5,"column":1,"index":154},"filename":"invalid-dynamically-construct-component-in-render.ts"},"fnName":"Example","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.js index 209cc83d65a2..8992b8bf7c75 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-construct-component-in-render.js @@ -1,4 +1,4 @@ -// @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { const Component = createComponent(); return ; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.expect.md index 374c28ffd19d..0882c4a10037 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { function Component() { return
; @@ -15,20 +15,12 @@ function Example(props) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { - const $ = _c(1); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - const Component = function Component() { - return
; - }; - t0 = ; - $[0] = t0; - } else { - t0 = $[0]; + function Component() { + return
; } - return t0; + return ; } ``` @@ -36,8 +28,8 @@ function Example(props) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render","details":[{"kind":"error","loc":{"start":{"line":6,"column":10,"index":130},"end":{"line":6,"column":19,"index":139},"filename":"invalid-dynamically-constructed-component-function.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":2,"index":73},"end":{"line":5,"column":3,"index":119},"filename":"invalid-dynamically-constructed-component-function.ts"},"message":"The component is created during render here"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":45},"end":{"line":7,"column":1,"index":145},"filename":"invalid-dynamically-constructed-component-function.ts"},"fnName":"Example","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render","details":[{"kind":"error","loc":{"start":{"line":6,"column":10,"index":149},"end":{"line":6,"column":19,"index":158},"filename":"invalid-dynamically-constructed-component-function.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":2,"index":92},"end":{"line":5,"column":3,"index":138},"filename":"invalid-dynamically-constructed-component-function.ts"},"message":"The component is created during render here"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":64},"end":{"line":7,"column":1,"index":164},"filename":"invalid-dynamically-constructed-component-function.ts"},"fnName":"Example","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.js index e48712dbf75c..123fb043eb19 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-function.js @@ -1,4 +1,4 @@ -// @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { function Component() { return
; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.expect.md index 860490581b1d..707a0a958502 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { const Component = props.foo.bar(); return ; @@ -13,27 +13,10 @@ function Example(props) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { - const $ = _c(4); - let t0; - if ($[0] !== props.foo) { - t0 = props.foo.bar(); - $[0] = props.foo; - $[1] = t0; - } else { - t0 = $[1]; - } - const Component = t0; - let t1; - if ($[2] !== Component) { - t1 = ; - $[2] = Component; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + const Component = props.foo.bar(); + return ; } ``` @@ -41,8 +24,8 @@ function Example(props) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render","details":[{"kind":"error","loc":{"start":{"line":4,"column":10,"index":118},"end":{"line":4,"column":19,"index":127},"filename":"invalid-dynamically-constructed-component-method-call.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":20,"index":91},"end":{"line":3,"column":35,"index":106},"filename":"invalid-dynamically-constructed-component-method-call.ts"},"message":"The component is created during render here"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":45},"end":{"line":5,"column":1,"index":133},"filename":"invalid-dynamically-constructed-component-method-call.ts"},"fnName":"Example","memoSlots":4,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render","details":[{"kind":"error","loc":{"start":{"line":4,"column":10,"index":137},"end":{"line":4,"column":19,"index":146},"filename":"invalid-dynamically-constructed-component-method-call.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":20,"index":110},"end":{"line":3,"column":35,"index":125},"filename":"invalid-dynamically-constructed-component-method-call.ts"},"message":"The component is created during render here"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":64},"end":{"line":5,"column":1,"index":152},"filename":"invalid-dynamically-constructed-component-method-call.ts"},"fnName":"Example","memoSlots":4,"memoBlocks":2,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.js index 7f43ffacbd45..7392c74adccb 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-method-call.js @@ -1,4 +1,4 @@ -// @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { const Component = props.foo.bar(); return ; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.expect.md index 8dbcf0f108af..2607ef63d8da 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { const Component = new ComponentFactory(); return ; @@ -13,18 +13,10 @@ function Example(props) { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { - const $ = _c(1); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - const Component = new ComponentFactory(); - t0 = ; - $[0] = t0; - } else { - t0 = $[0]; - } - return t0; + const Component = new ComponentFactory(); + return ; } ``` @@ -32,8 +24,8 @@ function Example(props) { ## Logs ``` -{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render","details":[{"kind":"error","loc":{"start":{"line":4,"column":10,"index":125},"end":{"line":4,"column":19,"index":134},"filename":"invalid-dynamically-constructed-component-new.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":20,"index":91},"end":{"line":3,"column":42,"index":113},"filename":"invalid-dynamically-constructed-component-new.ts"},"message":"The component is created during render here"}]}},"fnLoc":null} -{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":45},"end":{"line":5,"column":1,"index":140},"filename":"invalid-dynamically-constructed-component-new.ts"},"fnName":"Example","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileError","detail":{"options":{"category":"StaticComponents","reason":"Cannot create components during render","description":"Components created during render will reset their state each time they are created. Declare components outside of render","details":[{"kind":"error","loc":{"start":{"line":4,"column":10,"index":144},"end":{"line":4,"column":19,"index":153},"filename":"invalid-dynamically-constructed-component-new.ts"},"message":"This component is created during render"},{"kind":"error","loc":{"start":{"line":3,"column":20,"index":110},"end":{"line":3,"column":42,"index":132},"filename":"invalid-dynamically-constructed-component-new.ts"},"message":"The component is created during render here"}]}},"fnLoc":null} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":2,"column":0,"index":64},"end":{"line":5,"column":1,"index":159},"filename":"invalid-dynamically-constructed-component-new.ts"},"fnName":"Example","memoSlots":1,"memoBlocks":1,"memoValues":1,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.js index a735e076d998..4b4e3f7f0240 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/static-components/invalid-dynamically-constructed-component-new.js @@ -1,4 +1,4 @@ -// @loggerTestOnly @validateStaticComponents +// @loggerTestOnly @validateStaticComponents @outputMode:"lint" function Example(props) { const Component = new ComponentFactory(); return ; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-set-state-in-useEffect-from-ref.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-set-state-in-useEffect-from-ref.expect.md index e60888bafc64..8e9d259fa715 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-set-state-in-useEffect-from-ref.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-set-state-in-useEffect-from-ref.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoSetStateInEffects +// @validateNoSetStateInEffects @outputMode:"lint" import {useState, useRef, useEffect} from 'react'; function Tooltip() { @@ -27,28 +27,18 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoSetStateInEffects +// @validateNoSetStateInEffects @outputMode:"lint" import { useState, useRef, useEffect } from "react"; function Tooltip() { - const $ = _c(2); const ref = useRef(null); const [tooltipHeight, setTooltipHeight] = useState(0); - let t0; - let t1; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - const { height } = ref.current.getBoundingClientRect(); - setTooltipHeight(height); - }; - t1 = []; - $[0] = t0; - $[1] = t1; - } else { - t0 = $[0]; - t1 = $[1]; - } - useEffect(t0, t1); + + useEffect(() => { + const { height } = ref.current.getBoundingClientRect(); + setTooltipHeight(height); + }, []); + return tooltipHeight; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-set-state-in-useEffect-from-ref.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-set-state-in-useEffect-from-ref.js index e27292c0a8a8..88201926788c 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-set-state-in-useEffect-from-ref.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-set-state-in-useEffect-from-ref.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInEffects +// @validateNoSetStateInEffects @outputMode:"lint" import {useState, useRef, useEffect} from 'react'; function Tooltip() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-arithmetic.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-arithmetic.expect.md index ee19358f3fb0..845711d24be7 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-arithmetic.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-arithmetic.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @outputMode:"lint" import {useState, useRef, useLayoutEffect} from 'react'; function Component() { @@ -26,34 +26,17 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @outputMode:"lint" import { useState, useRef, useLayoutEffect } from "react"; function Component() { - const $ = _c(3); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = { size: 5 }; - $[0] = t0; - } else { - t0 = $[0]; - } - const ref = useRef(t0); + const ref = useRef({ size: 5 }); const [computedSize, setComputedSize] = useState(0); - let t1; - let t2; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = () => { - setComputedSize(ref.current.size * 10); - }; - t2 = []; - $[1] = t1; - $[2] = t2; - } else { - t1 = $[1]; - t2 = $[2]; - } - useLayoutEffect(t1, t2); + + useLayoutEffect(() => { + setComputedSize(ref.current.size * 10); + }, []); + return computedSize; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-arithmetic.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-arithmetic.js index d312b139b701..c6903f893327 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-arithmetic.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-arithmetic.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @outputMode:"lint" import {useState, useRef, useLayoutEffect} from 'react'; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-array-index.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-array-index.expect.md index a7c3714b18f8..269bda83ce96 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-array-index.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-array-index.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @outputMode:"lint" import {useState, useRef, useEffect} from 'react'; function Component() { @@ -27,34 +27,18 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @outputMode:"lint" import { useState, useRef, useEffect } from "react"; function Component() { - const $ = _c(3); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = [1, 2, 3, 4, 5]; - $[0] = t0; - } else { - t0 = $[0]; - } - const ref = useRef(t0); + const ref = useRef([1, 2, 3, 4, 5]); const [value, setValue] = useState(0); - let t1; - let t2; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = () => { - setValue(ref.current[2]); - }; - t2 = []; - $[1] = t1; - $[2] = t2; - } else { - t1 = $[1]; - t2 = $[2]; - } - useEffect(t1, t2); + + useEffect(() => { + const index = 2; + setValue(ref.current[index]); + }, []); + return value; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-array-index.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-array-index.js index 90459ac445cf..a5cfd5c2b2bd 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-array-index.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-array-index.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @outputMode:"lint" import {useState, useRef, useEffect} from 'react'; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-function-call.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-function-call.expect.md index 85796abb964f..ce186bcddf44 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-function-call.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-function-call.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @outputMode:"lint" import {useState, useRef, useEffect} from 'react'; function Component() { @@ -33,33 +33,24 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @outputMode:"lint" import { useState, useRef, useEffect } from "react"; function Component() { - const $ = _c(2); const ref = useRef(null); const [width, setWidth] = useState(0); - let t0; - let t1; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - const getBoundingRect = function getBoundingRect(ref_0) { - if (ref_0.current) { - return ref_0.current.getBoundingClientRect?.()?.width ?? 100; - } - return 100; - }; - setWidth(getBoundingRect(ref)); - }; - t1 = []; - $[0] = t0; - $[1] = t1; - } else { - t0 = $[0]; - t1 = $[1]; - } - useEffect(t0, t1); + + useEffect(() => { + function getBoundingRect(ref_0) { + if (ref_0.current) { + return ref_0.current.getBoundingClientRect?.()?.width ?? 100; + } + return 100; + } + + setWidth(getBoundingRect(ref)); + }, []); + return width; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-function-call.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-function-call.js index e37b3f3ea2b4..8b92e7c5fd0d 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-function-call.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-effect-from-ref-function-call.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @outputMode:"lint" import {useState, useRef, useEffect} from 'react'; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-controlled-by-ref-value.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-controlled-by-ref-value.expect.md index 182b44f642d7..6570731d290e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-controlled-by-ref-value.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-controlled-by-ref-value.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @loggerTestOnly @compilationMode:"infer" +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @loggerTestOnly @compilationMode:"infer" @outputMode:"lint" import {useState, useRef, useEffect} from 'react'; function Component({x, y}) { @@ -48,39 +48,26 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @loggerTestOnly @compilationMode:"infer" +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @loggerTestOnly @compilationMode:"infer" @outputMode:"lint" import { useState, useRef, useEffect } from "react"; -function Component(t0) { - const $ = _c(4); - const { x, y } = t0; +function Component({ x, y }) { const previousXRef = useRef(null); const previousYRef = useRef(null); const [data, setData] = useState(null); - let t1; - let t2; - if ($[0] !== x || $[1] !== y) { - t1 = () => { - const previousX = previousXRef.current; - previousXRef.current = x; - const previousY = previousYRef.current; - previousYRef.current = y; - if (!areEqual(x, previousX) || !areEqual(y, previousY)) { - const data_0 = load({ x, y }); - setData(data_0); - } - }; - t2 = [x, y]; - $[0] = x; - $[1] = y; - $[2] = t1; - $[3] = t2; - } else { - t1 = $[2]; - t2 = $[3]; - } - useEffect(t1, t2); + + useEffect(() => { + const previousX = previousXRef.current; + previousXRef.current = x; + const previousY = previousYRef.current; + previousYRef.current = y; + if (!areEqual(x, previousX) || !areEqual(y, previousY)) { + const data_0 = load({ x, y }); + setData(data_0); + } + }, [x, y]); + return data; } @@ -107,7 +94,7 @@ export const FIXTURE_ENTRYPOINT = { ## Logs ``` -{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":163},"end":{"line":22,"column":1,"index":631},"filename":"valid-setState-in-useEffect-controlled-by-ref-value.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":1,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} +{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":182},"end":{"line":22,"column":1,"index":650},"filename":"valid-setState-in-useEffect-controlled-by-ref-value.ts"},"fnName":"Component","memoSlots":4,"memoBlocks":1,"memoValues":2,"prunedMemoBlocks":0,"prunedMemoValues":0} ``` ### Eval output diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-controlled-by-ref-value.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-controlled-by-ref-value.js index 46f11057d118..4e884e1c65de 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-controlled-by-ref-value.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-controlled-by-ref-value.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @loggerTestOnly @compilationMode:"infer" +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @loggerTestOnly @compilationMode:"infer" @outputMode:"lint" import {useState, useRef, useEffect} from 'react'; function Component({x, y}) { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.expect.md index ac55bd046993..da69a338f8a6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoSetStateInEffects +// @validateNoSetStateInEffects @outputMode:"lint" import {useEffect, useState} from 'react'; function Component() { @@ -26,26 +26,17 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoSetStateInEffects +// @validateNoSetStateInEffects @outputMode:"lint" import { useEffect, useState } from "react"; function Component() { - const $ = _c(1); const [state, setState] = useState(0); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - const f = () => { - setState(); - }; - - setTimeout(() => f(), 10); + useEffect(() => { + const f = () => { + setState(); }; - $[0] = t0; - } else { - t0 = $[0]; - } - useEffect(t0); + setTimeout(() => f(), 10); + }); return state; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.js index 525f3e97d191..f5255fd4b508 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener-transitive.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInEffects +// @validateNoSetStateInEffects @outputMode:"lint" import {useEffect, useState} from 'react'; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.expect.md index a7deed9afb09..7a5718a17103 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoSetStateInEffects +// @validateNoSetStateInEffects @outputMode:"lint" import {useEffect, useState} from 'react'; function Component() { @@ -23,22 +23,14 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoSetStateInEffects +// @validateNoSetStateInEffects @outputMode:"lint" import { useEffect, useState } from "react"; function Component() { - const $ = _c(1); const [state, setState] = useState(0); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - setTimeout(setState, 10); - }; - $[0] = t0; - } else { - t0 = $[0]; - } - useEffect(t0); + useEffect(() => { + setTimeout(setState, 10); + }); return state; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.js index 723e4841f6d3..68f5da981fc6 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useEffect-listener.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInEffects +// @validateNoSetStateInEffects @outputMode:"lint" import {useEffect, useState} from 'react'; function Component() { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useLayoutEffect-from-ref.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useLayoutEffect-from-ref.expect.md index cacd46bfe42a..9a926dc2231e 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useLayoutEffect-from-ref.expect.md +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useLayoutEffect-from-ref.expect.md @@ -2,7 +2,7 @@ ## Input ```javascript -// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @outputMode:"lint" import {useState, useRef, useLayoutEffect} from 'react'; function Tooltip() { @@ -27,28 +27,18 @@ export const FIXTURE_ENTRYPOINT = { ## Code ```javascript -import { c as _c } from "react/compiler-runtime"; // @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @outputMode:"lint" import { useState, useRef, useLayoutEffect } from "react"; function Tooltip() { - const $ = _c(2); const ref = useRef(null); const [tooltipHeight, setTooltipHeight] = useState(0); - let t0; - let t1; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = () => { - const { height } = ref.current.getBoundingClientRect(); - setTooltipHeight(height); - }; - t1 = []; - $[0] = t0; - $[1] = t1; - } else { - t0 = $[0]; - t1 = $[1]; - } - useLayoutEffect(t0, t1); + + useLayoutEffect(() => { + const { height } = ref.current.getBoundingClientRect(); + setTooltipHeight(height); + }, []); + return tooltipHeight; } diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useLayoutEffect-from-ref.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useLayoutEffect-from-ref.js index 339b550730d7..b41b2c1e7ce5 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useLayoutEffect-from-ref.js +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/valid-setState-in-useLayoutEffect-from-ref.js @@ -1,4 +1,4 @@ -// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects +// @validateNoSetStateInEffects @enableAllowSetStateFromRefsInEffects @outputMode:"lint" import {useState, useRef, useLayoutEffect} from 'react'; function Tooltip() {