diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index 7eef0a7cc24b..ca03b8a7b1e3 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -223,7 +223,7 @@ const EnvironmentConfigSchema = z.object({ validateHooksUsage: z.boolean().default(true), // Validate that ref values (`ref.current`) are not accessed during render. - validateRefAccessDuringRender: z.boolean().default(false), + validateRefAccessDuringRender: z.boolean().default(true), /* * Validates that setState is not unconditionally called during render, as it can lead to diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capture-ref-for-later-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capture-ref-for-later-mutation.expect.md deleted file mode 100644 index b7371108d586..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capture-ref-for-later-mutation.expect.md +++ /dev/null @@ -1,69 +0,0 @@ - -## Input - -```javascript -import {useRef} from 'react'; -import {addOne} from 'shared-runtime'; - -function useKeyCommand() { - const currentPosition = useRef(0); - const handleKey = direction => () => { - const position = currentPosition.current; - const nextPosition = direction === 'left' ? addOne(position) : position; - currentPosition.current = nextPosition; - }; - const moveLeft = { - handler: handleKey('left'), - }; - const moveRight = { - handler: handleKey('right'), - }; - return [moveLeft, moveRight]; -} - -export const FIXTURE_ENTRYPOINT = { - fn: useKeyCommand, - params: [], -}; - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; -import { useRef } from "react"; -import { addOne } from "shared-runtime"; - -function useKeyCommand() { - const $ = _c(1); - const currentPosition = useRef(0); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - const handleKey = (direction) => () => { - const position = currentPosition.current; - const nextPosition = direction === "left" ? addOne(position) : position; - currentPosition.current = nextPosition; - }; - - const moveLeft = { handler: handleKey("left") }; - - const moveRight = { handler: handleKey("right") }; - - t0 = [moveLeft, moveRight]; - $[0] = t0; - } else { - t0 = $[0]; - } - return t0; -} - -export const FIXTURE_ENTRYPOINT = { - fn: useKeyCommand, - params: [], -}; - -``` - -### Eval output -(kind: ok) [{"handler":"[[ function params=0 ]]"},{"handler":"[[ function params=0 ]]"}] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-later-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-later-mutation.expect.md new file mode 100644 index 000000000000..52350036257d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-later-mutation.expect.md @@ -0,0 +1,50 @@ + +## Input + +```javascript +import {useRef} from 'react'; +import {addOne} from 'shared-runtime'; + +function useKeyCommand() { + const currentPosition = useRef(0); + const handleKey = direction => () => { + const position = currentPosition.current; + const nextPosition = direction === 'left' ? addOne(position) : position; + currentPosition.current = nextPosition; + }; + const moveLeft = { + handler: handleKey('left'), + }; + const moveRight = { + handler: handleKey('right'), + }; + return [moveLeft, moveRight]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useKeyCommand, + params: [], +}; + +``` + + +## Error + +``` + 10 | }; + 11 | const moveLeft = { +> 12 | handler: handleKey('left'), + | ^^^^^^^^^ InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) (12:12) + +InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (12:12) + +InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) (15:15) + +InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (15:15) + 13 | }; + 14 | const moveRight = { + 15 | handler: handleKey('right'), +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capture-ref-for-later-mutation.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-later-mutation.tsx similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/capture-ref-for-later-mutation.tsx rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.capture-ref-for-later-mutation.tsx diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-ref-mutable-range.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-ref-mutable-range.expect.md new file mode 100644 index 000000000000..1e5fda2a35fb --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-ref-mutable-range.expect.md @@ -0,0 +1,40 @@ + +## Input + +```javascript +import {Stringify, identity, mutate, CONST_TRUE} from 'shared-runtime'; + +function Foo(props, ref) { + const value = {}; + if (CONST_TRUE) { + mutate(value); + return ; + } + mutate(value); + if (CONST_TRUE) { + return ; + } + return value; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Foo, + params: [{}, {current: 'fake-ref-object'}], +}; + +``` + + +## Error + +``` + 9 | mutate(value); + 10 | if (CONST_TRUE) { +> 11 | return ; + | ^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (11:11) + 12 | } + 13 | return value; + 14 | } +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-ref-mutable-range.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-ref-mutable-range.tsx similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-ref-mutable-range.tsx rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.repro-ref-mutable-range.tsx diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md new file mode 100644 index 000000000000..dbc3060a26f4 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md @@ -0,0 +1,42 @@ + +## Input + +```javascript +// @enablePreserveExistingMemoizationGuarantees:false +import {useCallback, useRef} from 'react'; + +function Component(props) { + const ref = useRef({inner: null}); + + const onChange = useCallback(event => { + // The ref should still be mutable here even though function deps are frozen in + // @enablePreserveExistingMemoizationGuarantees mode + ref.current.inner = event.target.value; + }); + + ref.current.inner = null; + + return ; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{}], +}; + +``` + + +## Error + +``` + 11 | }); + 12 | +> 13 | ref.current.inner = null; + | ^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (13:13) + 14 | + 15 | return ; + 16 | } +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-set-ref-nested-property-dont-preserve-memoization.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-set-ref-nested-property-dont-preserve-memoization.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-set-ref-nested-property-dont-preserve-memoization.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.useCallback-set-ref-nested-property-dont-preserve-memoization.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/capture-ref-for-later-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/capture-ref-for-later-mutation.expect.md deleted file mode 100644 index 54184be0f3f3..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/capture-ref-for-later-mutation.expect.md +++ /dev/null @@ -1,70 +0,0 @@ - -## Input - -```javascript -// @enableReactiveScopesInHIR:false -import {useRef} from 'react'; -import {addOne} from 'shared-runtime'; - -function useKeyCommand() { - const currentPosition = useRef(0); - const handleKey = direction => () => { - const position = currentPosition.current; - const nextPosition = direction === 'left' ? addOne(position) : position; - currentPosition.current = nextPosition; - }; - const moveLeft = { - handler: handleKey('left'), - }; - const moveRight = { - handler: handleKey('right'), - }; - return [moveLeft, moveRight]; -} - -export const FIXTURE_ENTRYPOINT = { - fn: useKeyCommand, - params: [], -}; - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; // @enableReactiveScopesInHIR:false -import { useRef } from "react"; -import { addOne } from "shared-runtime"; - -function useKeyCommand() { - const $ = _c(1); - const currentPosition = useRef(0); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - const handleKey = (direction) => () => { - const position = currentPosition.current; - const nextPosition = direction === "left" ? addOne(position) : position; - currentPosition.current = nextPosition; - }; - - const moveLeft = { handler: handleKey("left") }; - - const moveRight = { handler: handleKey("right") }; - - t0 = [moveLeft, moveRight]; - $[0] = t0; - } else { - t0 = $[0]; - } - return t0; -} - -export const FIXTURE_ENTRYPOINT = { - fn: useKeyCommand, - params: [], -}; - -``` - -### Eval output -(kind: ok) [{"handler":"[[ function params=0 ]]"},{"handler":"[[ function params=0 ]]"}] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/error.capture-ref-for-later-mutation.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/error.capture-ref-for-later-mutation.expect.md new file mode 100644 index 000000000000..f0b0e6f3a867 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/error.capture-ref-for-later-mutation.expect.md @@ -0,0 +1,51 @@ + +## Input + +```javascript +// @enableReactiveScopesInHIR:false +import {useRef} from 'react'; +import {addOne} from 'shared-runtime'; + +function useKeyCommand() { + const currentPosition = useRef(0); + const handleKey = direction => () => { + const position = currentPosition.current; + const nextPosition = direction === 'left' ? addOne(position) : position; + currentPosition.current = nextPosition; + }; + const moveLeft = { + handler: handleKey('left'), + }; + const moveRight = { + handler: handleKey('right'), + }; + return [moveLeft, moveRight]; +} + +export const FIXTURE_ENTRYPOINT = { + fn: useKeyCommand, + params: [], +}; + +``` + + +## Error + +``` + 11 | }; + 12 | const moveLeft = { +> 13 | handler: handleKey('left'), + | ^^^^^^^^^ InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) (13:13) + +InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (13:13) + +InvalidReact: This function accesses a ref value (the `current` property), which may not be accessed during render. (https://react.dev/reference/react/useRef) (16:16) + +InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (16:16) + 14 | }; + 15 | const moveRight = { + 16 | handler: handleKey('right'), +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/capture-ref-for-later-mutation.tsx b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/error.capture-ref-for-later-mutation.tsx similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/capture-ref-for-later-mutation.tsx rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/original-reactive-scopes-fork/error.capture-ref-for-later-mutation.tsx diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.maybe-mutable-ref-not-preserved.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.maybe-mutable-ref-not-preserved.expect.md new file mode 100644 index 000000000000..1ac3884f92f2 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.maybe-mutable-ref-not-preserved.expect.md @@ -0,0 +1,35 @@ + +## Input + +```javascript +// @validatePreserveExistingMemoizationGuarantees:true + +import {useRef, useMemo} from 'react'; +import {makeArray} from 'shared-runtime'; + +function useFoo() { + const r = useRef(); + return useMemo(() => makeArray(r), []); +} + +export const FIXTURE_ENTRYPOINT = { + fn: useFoo, + params: [], +}; + +``` + + +## Error + +``` + 6 | function useFoo() { + 7 | const r = useRef(); +> 8 | return useMemo(() => makeArray(r), []); + | ^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (8:8) + 9 | } + 10 | + 11 | export const FIXTURE_ENTRYPOINT = { +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/maybe-mutable-ref-not-preserved.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.maybe-mutable-ref-not-preserved.ts similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/maybe-mutable-ref-not-preserved.ts rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.maybe-mutable-ref-not-preserved.ts diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-with-refs.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-with-refs.flow.expect.md new file mode 100644 index 000000000000..de37e0d3d439 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-with-refs.flow.expect.md @@ -0,0 +1,31 @@ + +## Input + +```javascript +// @flow @validatePreserveExistingMemoizationGuarantees +import {identity} from 'shared-runtime'; + +component Component(disableLocalRef, ref) { + const localRef = useFooRef(); + const mergedRef = useMemo(() => { + return disableLocalRef ? ref : identity(ref, localRef); + }, [disableLocalRef, ref, localRef]); + return
; +} + +``` + + +## Error + +``` + 5 | const localRef = useFooRef(); + 6 | const mergedRef = useMemo(() => { +> 7 | return disableLocalRef ? ref : identity(ref, localRef); + | ^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (7:7) + 8 | }, [disableLocalRef, ref, localRef]); + 9 | return
; + 10 | } +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-with-refs.flow.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-with-refs.flow.js similarity index 100% rename from compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-with-refs.flow.js rename to compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.useMemo-with-refs.flow.js diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/maybe-mutable-ref-not-preserved.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/maybe-mutable-ref-not-preserved.expect.md deleted file mode 100644 index 61ab6bafaecd..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/maybe-mutable-ref-not-preserved.expect.md +++ /dev/null @@ -1,53 +0,0 @@ - -## Input - -```javascript -// @validatePreserveExistingMemoizationGuarantees:true - -import {useRef, useMemo} from 'react'; -import {makeArray} from 'shared-runtime'; - -function useFoo() { - const r = useRef(); - return useMemo(() => makeArray(r), []); -} - -export const FIXTURE_ENTRYPOINT = { - fn: useFoo, - params: [], -}; - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees:true - -import { useRef, useMemo } from "react"; -import { makeArray } from "shared-runtime"; - -function useFoo() { - const $ = _c(1); - const r = useRef(); - let t0; - let t1; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t1 = makeArray(r); - $[0] = t1; - } else { - t1 = $[0]; - } - t0 = t1; - return t0; -} - -export const FIXTURE_ENTRYPOINT = { - fn: useFoo, - params: [], -}; - -``` - -### Eval output -(kind: ok) [{}] \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-with-refs.flow.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-with-refs.flow.expect.md deleted file mode 100644 index 1fc118ac59ba..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/useMemo-with-refs.flow.expect.md +++ /dev/null @@ -1,54 +0,0 @@ - -## Input - -```javascript -// @flow @validatePreserveExistingMemoizationGuarantees -import {identity} from 'shared-runtime'; - -component Component(disableLocalRef, ref) { - const localRef = useFooRef(); - const mergedRef = useMemo(() => { - return disableLocalRef ? ref : identity(ref, localRef); - }, [disableLocalRef, ref, localRef]); - return
; -} - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; -import { identity } from "shared-runtime"; - -const Component = React.forwardRef(Component_withRef); -function Component_withRef(t0, ref) { - const $ = _c(6); - const { disableLocalRef } = t0; - const localRef = useFooRef(); - let t1; - let t2; - if ($[0] !== disableLocalRef || $[1] !== ref || $[2] !== localRef) { - t2 = disableLocalRef ? ref : identity(ref, localRef); - $[0] = disableLocalRef; - $[1] = ref; - $[2] = localRef; - $[3] = t2; - } else { - t2 = $[3]; - } - t1 = t2; - const mergedRef = t1; - let t3; - if ($[4] !== mergedRef) { - t3 =
; - $[4] = mergedRef; - $[5] = t3; - } else { - t3 = $[5]; - } - return t3; -} - -``` - \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-ref-mutable-range.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-ref-mutable-range.expect.md deleted file mode 100644 index 8b656b8e23b2..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-ref-mutable-range.expect.md +++ /dev/null @@ -1,89 +0,0 @@ - -## Input - -```javascript -import {Stringify, identity, mutate, CONST_TRUE} from 'shared-runtime'; - -function Foo(props, ref) { - const value = {}; - if (CONST_TRUE) { - mutate(value); - return ; - } - mutate(value); - if (CONST_TRUE) { - return ; - } - return value; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Foo, - params: [{}, {current: 'fake-ref-object'}], -}; - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; -import { Stringify, identity, mutate, CONST_TRUE } from "shared-runtime"; - -function Foo(props, ref) { - const $ = _c(7); - let value; - let t0; - if ($[0] !== ref) { - t0 = Symbol.for("react.early_return_sentinel"); - bb0: { - value = {}; - if (CONST_TRUE) { - mutate(value); - t0 = ; - break bb0; - } - - mutate(value); - } - $[0] = ref; - $[1] = value; - $[2] = t0; - } else { - value = $[1]; - t0 = $[2]; - } - if (t0 !== Symbol.for("react.early_return_sentinel")) { - return t0; - } - if (CONST_TRUE) { - let t1; - if ($[3] !== ref) { - t1 = identity(ref); - $[3] = ref; - $[4] = t1; - } else { - t1 = $[4]; - } - let t2; - if ($[5] !== t1) { - t2 = ; - $[5] = t1; - $[6] = t2; - } else { - t2 = $[6]; - } - return t2; - } - return value; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Foo, - params: [{}, { current: "fake-ref-object" }], -}; - -``` - -### Eval output -(kind: ok)
{"ref":{"current":"fake-ref-object"}}
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md deleted file mode 100644 index 1efca9f0066b..000000000000 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useCallback-set-ref-nested-property-dont-preserve-memoization.expect.md +++ /dev/null @@ -1,75 +0,0 @@ - -## Input - -```javascript -// @enablePreserveExistingMemoizationGuarantees:false -import {useCallback, useRef} from 'react'; - -function Component(props) { - const ref = useRef({inner: null}); - - const onChange = useCallback(event => { - // The ref should still be mutable here even though function deps are frozen in - // @enablePreserveExistingMemoizationGuarantees mode - ref.current.inner = event.target.value; - }); - - ref.current.inner = null; - - return ; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{}], -}; - -``` - -## Code - -```javascript -import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingMemoizationGuarantees:false -import { useCallback, useRef } from "react"; - -function Component(props) { - const $ = _c(3); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = { inner: null }; - $[0] = t0; - } else { - t0 = $[0]; - } - const ref = useRef(t0); - let t1; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = (event) => { - ref.current.inner = event.target.value; - }; - $[1] = t1; - } else { - t1 = $[1]; - } - const onChange = t1; - - ref.current.inner = null; - let t2; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t2 = ; - $[2] = t2; - } else { - t2 = $[2]; - } - return t2; -} - -export const FIXTURE_ENTRYPOINT = { - fn: Component, - params: [{}], -}; - -``` - -### Eval output -(kind: ok) \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts b/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts index 8150a523263c..706563b33b45 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/parseConfigPragma-test.ts @@ -14,16 +14,16 @@ describe('parseConfigPragma()', () => { // Validate defaults first to make sure that the parser is getting the value from the pragma, // and not just missing it and getting the default value expect(defaultConfig.enableUseTypeAnnotations).toBe(false); - expect(defaultConfig.validateRefAccessDuringRender).toBe(false); + expect(defaultConfig.validateNoSetStateInPassiveEffects).toBe(false); expect(defaultConfig.validateNoSetStateInRender).toBe(true); const config = parseConfigPragma( - '@enableUseTypeAnnotations @validateRefAccessDuringRender:true @validateNoSetStateInRender:false', + '@enableUseTypeAnnotations @validateNoSetStateInPassiveEffects:true @validateNoSetStateInRender:false', ); expect(config).toEqual({ ...defaultConfig, enableUseTypeAnnotations: true, - validateRefAccessDuringRender: true, + validateNoSetStateInPassiveEffects: true, validateNoSetStateInRender: false, }); }); diff --git a/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts b/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts index c87bcadbe98b..c1aba9f4c840 100644 --- a/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts +++ b/compiler/packages/eslint-plugin-react-compiler/__tests__/ReactCompilerRule-test.ts @@ -93,12 +93,12 @@ const tests: CompilerTestCases = { `, }, { - // TODO(gsn): Move this to invalid test suite, when we turn on - // validateRefAccessDuringRender validation + // Don't report the issue if Flow already has name: '[InvalidInput] Ref access during render', code: normalizeIndent` function Component(props) { const ref = useRef(null); + // $FlowFixMe[react-rule-unsafe-ref] const value = ref.current; return value; } @@ -106,6 +106,22 @@ const tests: CompilerTestCases = { }, ], invalid: [ + { + name: '[InvalidInput] Ref access during render', + code: normalizeIndent` + function Component(props) { + const ref = useRef(null); + const value = ref.current; + return value; + } + `, + errors: [ + { + message: + 'Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)', + }, + ], + }, { name: 'Reportable levels can be configured', options: [{reportableLevels: new Set([ErrorSeverity.Todo])}], diff --git a/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts b/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts index 2fe34a0b7ff2..7b6525842c4d 100644 --- a/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts +++ b/compiler/packages/eslint-plugin-react-compiler/src/rules/ReactCompilerRule.ts @@ -179,7 +179,10 @@ const rule: Rule.RuleModule = { if (!isReportableDiagnostic(detail)) { return; } - if (hasFlowSuppression(detail.loc, 'react-rule-hook')) { + if ( + hasFlowSuppression(detail.loc, 'react-rule-hook') || + hasFlowSuppression(detail.loc, 'react-rule-unsafe-ref') + ) { // If Flow already caught this error, we don't need to report it again. return; }