diff --git a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts index d00302559bb4e..6677d3c681569 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateNoRefAccessInRender.ts @@ -403,7 +403,9 @@ function validateNoRefAccessInRenderImpl( } } for (const operand of eachInstructionValueOperand(instr.value)) { - if (hookKind != null) { + if (hookKind === 'useState') { + validateNoRefValueAccess(errors, env, operand); + } else if (hookKind != null) { validateNoDirectRefValueAccess(errors, operand, env); } else { validateNoRefAccess(errors, env, operand, operand.loc); diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ref-in-useState.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ref-in-useState.expect.md new file mode 100644 index 0000000000000..ca65b3e1729c0 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ref-in-useState.expect.md @@ -0,0 +1,27 @@ + +## Input + +```javascript +// @validateRefAccessDuringRender +function Component(props) { + const ref = useRef(1); + const [state] = useState(() => ref.current); + return
{state}
; +} + +``` + + +## Error + +``` + 2 | function Component(props) { + 3 | const ref = useRef(1); +> 4 | const [state] = useState(() => ref.current); + | ^^^^^^^^^^^^^^^^^ InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef) (4:4) + 5 | return
{state}
; + 6 | } + 7 | +``` + + \ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ref-in-useState.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ref-in-useState.js new file mode 100644 index 0000000000000..3e3367d23011a --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-ref-in-useState.js @@ -0,0 +1,6 @@ +// @validateRefAccessDuringRender +function Component(props) { + const ref = useRef(1); + const [state] = useState(() => ref.current); + return
{state}
; +} diff --git a/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts b/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts index 30762e5819535..e752423555df9 100644 --- a/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts +++ b/packages/eslint-plugin-react-hooks/__tests__/ReactCompilerRule-test.ts @@ -94,8 +94,53 @@ const tests: CompilerTestCases = { } `, }, + { + name: 'Ref access in useEffect hook', + code: normalizeIndent` + function Component() { + const ref = useRef(1); + useEffect(() => { + ref.current = 2 * ref.current; + }, []); + return
Hello world
; + } + `, + }, ], + 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: '[InvalidInput] Ref access in useState initial value function', + code: normalizeIndent` + function Component(props) { + const ref = useRef(1); + const [value] = useState(() => 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])}],