diff --git a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts index 1c4443e5a49..55974db14ce 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/TypeInference/InferTypes.ts @@ -393,7 +393,7 @@ function* generateInstructionTypes( shapeId: BuiltInArrayId, }); } else { - break; + continue; } } } else { diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-render-unbound-state.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-render-unbound-state.expect.md new file mode 100644 index 00000000000..423076cc3a4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-render-unbound-state.expect.md @@ -0,0 +1,41 @@ + +## Input + +```javascript +function Component(props) { + // Intentionally don't bind state, this repros a bug where we didn't + // infer the type of destructured properties after a hole in the array + let [, setState] = useState(); + setState(1); + return props.foo; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: ['TodoAdd'], + isComponent: 'TodoAdd', +}; + +``` + + +## Error + +``` +Found 1 error: + +Error: Calling setState during render may trigger an infinite loop + +Calling setState during render will trigger another render, and can lead to infinite loops. (https://react.dev/reference/react/useState). + +error.invalid-setState-in-render-unbound-state.ts:5:2 + 3 | // infer the type of destructured properties after a hole in the array + 4 | let [, setState] = useState(); +> 5 | setState(1); + | ^^^^^^^^ Found setState() in render + 6 | return props.foo; + 7 | } + 8 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-render-unbound-state.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-render-unbound-state.js new file mode 100644 index 00000000000..58e2837692a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-setState-in-render-unbound-state.js @@ -0,0 +1,13 @@ +function Component(props) { + // Intentionally don't bind state, this repros a bug where we didn't + // infer the type of destructured properties after a hole in the array + let [, setState] = useState(); + setState(1); + return props.foo; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: ['TodoAdd'], + isComponent: 'TodoAdd', +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/holey-array.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/holey-array.expect.md deleted file mode 100644 index 160699b115b..00000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/holey-array.expect.md +++ /dev/null @@ -1,35 +0,0 @@ - -## Input - -```javascript -function t(props) { - let [, setstate] = useState(); - setstate(1); - return props.foo; -} - -export const FIXTURE_ENTRYPOINT = { - fn: t, - params: ['TodoAdd'], - isComponent: 'TodoAdd', -}; - -``` - -## Code - -```javascript -function t(props) { - const [, setstate] = useState(); - setstate(1); - return props.foo; -} - -export const FIXTURE_ENTRYPOINT = { - fn: t, - params: ["TodoAdd"], - isComponent: "TodoAdd", -}; - -``` - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/holey-array.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/holey-array.js deleted file mode 100644 index d76b88f5791..00000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/holey-array.js +++ /dev/null @@ -1,11 +0,0 @@ -function t(props) { - let [, setstate] = useState(); - setstate(1); - return props.foo; -} - -export const FIXTURE_ENTRYPOINT = { - fn: t, - params: ['TodoAdd'], - isComponent: 'TodoAdd', -}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-use-memo-transition-no-ispending.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-use-memo-transition-no-ispending.expect.md new file mode 100644 index 00000000000..c5bfe197c3a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-use-memo-transition-no-ispending.expect.md @@ -0,0 +1,52 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees +import {useCallback, useTransition} from 'react'; + +function useFoo() { + const [, /* isPending intentionally not captured */ start] = useTransition(); + + return useCallback(() => { + start(); + }, []); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees +import { useCallback, useTransition } from "react"; + +function useFoo() { + const $ = _c(1); + const [, start] = useTransition(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = () => { + start(); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [], +}; + +``` + +### Eval output +(kind: ok) "[[ function params=0 ]]" \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-use-memo-transition-no-ispending.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-use-memo-transition-no-ispending.js new file mode 100644 index 00000000000..d7560197f03 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-use-memo-transition-no-ispending.js @@ -0,0 +1,15 @@ +// @validatePreserveExistingMemoizationGuarantees +import {useCallback, useTransition} from 'react'; + +function useFoo() { + const [, /* isPending intentionally not captured */ start] = useTransition(); + + return useCallback(() => { + start(); + }, []); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [], +}; diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-use-memo-unused-state.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-use-memo-unused-state.expect.md new file mode 100644 index 00000000000..84441267558 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-use-memo-unused-state.expect.md @@ -0,0 +1,55 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees +import {useCallback, useTransition} from 'react'; + +function useFoo() { + const [, /* state value intentionally not captured */ setState] = useState(); + + return useCallback(() => { + setState(x => x + 1); + }, []); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [], +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees +import { useCallback, useTransition } from "react"; + +function useFoo() { + const $ = _c(1); + const [, setState] = useState(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = () => { + setState(_temp); + }; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} +function _temp(x) { + return x + 1; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [], +}; + +``` + +### Eval output +(kind: exception) useState is not defined \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-use-memo-unused-state.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-use-memo-unused-state.js new file mode 100644 index 00000000000..e270d4f0192 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-use-memo-unused-state.js @@ -0,0 +1,15 @@ +// @validatePreserveExistingMemoizationGuarantees +import {useCallback, useTransition} from 'react'; + +function useFoo() { + const [, /* state value intentionally not captured */ setState] = useState(); + + return useCallback(() => { + setState(x => x + 1); + }, []); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [], +};