-
Notifications
You must be signed in to change notification settings - Fork 47.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[compiler] Treat ref-like named objects as refs
If a component uses the `useRef` hook directly then we type it's return value as a ref. But if it's wrapped in a custom hook then we lose out on this type information as the compiler doesn't look at the hook definition. This has resulted in some false positives in our analysis like the ones reported in #29160 and #29196. This PR will treat objects named as `ref` or if their names end with the substring `Ref`, and contain a property named `current`, as React refs. ``` const ref = useMyRef(); const myRef = useMyRef2(); useEffect(() => { ref.current = ...; myRef.current = ...; }) ``` In the above example, `ref` and `myRef` will be treated as React refs.
- Loading branch information
Showing
11 changed files
with
455 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
...-compiler/src/__tests__/fixtures/compiler/error.ref-like-name-not-ref.expect.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
|
||
## Input | ||
|
||
```javascript | ||
// @validatePreserveExistingMemoizationGuarantees | ||
import { useCallback, useRef } from "react"; | ||
|
||
function useCustomRef() { | ||
return useRef({ click: () => {} }); | ||
} | ||
|
||
function Foo() { | ||
const Ref = useCustomRef(); | ||
|
||
const onClick = useCallback(() => { | ||
Ref.current?.click(); | ||
}, []); | ||
|
||
return <button onClick={onClick} />; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: Foo, | ||
params: [], | ||
isComponent: true, | ||
}; | ||
|
||
``` | ||
## Error | ||
``` | ||
9 | const Ref = useCustomRef(); | ||
10 | | ||
> 11 | const onClick = useCallback(() => { | ||
| ^^^^^^^ | ||
> 12 | Ref.current?.click(); | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
> 13 | }, []); | ||
| ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected (11:13) | ||
14 | | ||
15 | return <button onClick={onClick} />; | ||
16 | } | ||
``` | ||
22 changes: 22 additions & 0 deletions
22
...abel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.ref-like-name-not-ref.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// @validatePreserveExistingMemoizationGuarantees | ||
import { useCallback, useRef } from "react"; | ||
|
||
function useCustomRef() { | ||
return useRef({ click: () => {} }); | ||
} | ||
|
||
function Foo() { | ||
const Ref = useCustomRef(); | ||
|
||
const onClick = useCallback(() => { | ||
Ref.current?.click(); | ||
}, []); | ||
|
||
return <button onClick={onClick} />; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: Foo, | ||
params: [], | ||
isComponent: true, | ||
}; |
81 changes: 81 additions & 0 deletions
81
...eact-compiler/src/__tests__/fixtures/compiler/ref-like-name-as-refs-2.expect.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
|
||
## Input | ||
|
||
```javascript | ||
// @enableTreatRefLikeIdentifiersAsRefs @validatePreserveExistingMemoizationGuarantees | ||
import { useRef, useCallback } from "react"; | ||
|
||
function useCustomRef() { | ||
return useRef({ click: () => {} }); | ||
} | ||
|
||
function Foo() { | ||
const ref = useCustomRef(); | ||
|
||
const onClick = useCallback(() => { | ||
ref.current?.click(); | ||
}, []); | ||
|
||
return <button onClick={onClick} />; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: Foo, | ||
params: [], | ||
isComponent: true, | ||
}; | ||
|
||
``` | ||
## Code | ||
```javascript | ||
import { c as _c } from "react/compiler-runtime"; // @enableTreatRefLikeIdentifiersAsRefs @validatePreserveExistingMemoizationGuarantees | ||
import { useRef, useCallback } from "react"; | ||
|
||
function useCustomRef() { | ||
const $ = _c(1); | ||
let t0; | ||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) { | ||
t0 = { click: () => {} }; | ||
$[0] = t0; | ||
} else { | ||
t0 = $[0]; | ||
} | ||
return useRef(t0); | ||
} | ||
|
||
function Foo() { | ||
const $ = _c(3); | ||
const ref = useCustomRef(); | ||
let t0; | ||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) { | ||
t0 = () => { | ||
ref.current?.click(); | ||
}; | ||
$[0] = t0; | ||
} else { | ||
t0 = $[0]; | ||
} | ||
const onClick = t0; | ||
let t1; | ||
if ($[1] !== onClick) { | ||
t1 = <button onClick={onClick} />; | ||
$[1] = onClick; | ||
$[2] = t1; | ||
} else { | ||
t1 = $[2]; | ||
} | ||
return t1; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: Foo, | ||
params: [], | ||
isComponent: true, | ||
}; | ||
|
||
``` | ||
### Eval output | ||
(kind: ok) <button></button> |
22 changes: 22 additions & 0 deletions
22
...es/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/ref-like-name-as-refs-2.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// @enableTreatRefLikeIdentifiersAsRefs @validatePreserveExistingMemoizationGuarantees | ||
import { useRef, useCallback } from "react"; | ||
|
||
function useCustomRef() { | ||
return useRef({ click: () => {} }); | ||
} | ||
|
||
function Foo() { | ||
const ref = useCustomRef(); | ||
|
||
const onClick = useCallback(() => { | ||
ref.current?.click(); | ||
}, []); | ||
|
||
return <button onClick={onClick} />; | ||
} | ||
|
||
export const FIXTURE_ENTRYPOINT = { | ||
fn: Foo, | ||
params: [], | ||
isComponent: true, | ||
}; |
Oops, something went wrong.