diff --git a/compiler/forget/src/HIR/HIR.ts b/compiler/forget/src/HIR/HIR.ts index 80d405268a..661187ed90 100644 --- a/compiler/forget/src/HIR/HIR.ts +++ b/compiler/forget/src/HIR/HIR.ts @@ -979,6 +979,14 @@ export function isPrimitiveType(id: Identifier): boolean { return id.type.kind === "Primitive"; } +export function isRefValueType(id: Identifier): boolean { + return id.type.kind === "Object" && id.type.shapeId === "BuiltInRefValue"; +} + +export function isUseRefType(id: Identifier): boolean { + return id.type.kind === "Object" && id.type.shapeId === "BuiltInUseRefId"; +} + export function getHookKind(env: Environment, id: Identifier): HookKind | null { const idType = id.type; if (idType.kind === "Function") { diff --git a/compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts b/compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts index 05bb66b133..460d630d2c 100644 --- a/compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts +++ b/compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts @@ -11,6 +11,7 @@ import { IdentifierId, InstructionId, InstructionKind, + isUseRefType, makeInstructionId, Place, ReactiveFunction, @@ -314,6 +315,14 @@ class Context { // Checks if identifier is a valid dependency in the current scope #checkValidDependency(maybeDependency: ReactiveScopeDependency): boolean { + // ref.current access is not a valid dep + if ( + isUseRefType(maybeDependency.identifier) && + maybeDependency.path.at(0) === "current" + ) { + return false; + } + const identifier = maybeDependency.identifier; // If this operand is used in a scope, has a dynamic value, and was defined // before this scope, then its a dependency of the scope. diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ref-current-field-not-added-to-dep.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-field-not-added-to-dep.expect.md new file mode 100644 index 0000000000..7a630ff951 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-field-not-added-to-dep.expect.md @@ -0,0 +1,46 @@ + +## Input + +```javascript +function VideoTab() { + const ref = useRef(); + let x = () => { + ref.current.x; + }; + + return ; +} + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +function VideoTab() { + const $ = useMemoCache(3); + const ref = useRef(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = () => { + ref.current.x; + }; + $[0] = t0; + } else { + t0 = $[0]; + } + const x = t0; + const c_1 = $[1] !== x; + let t1; + if (c_1) { + t1 = ; + $[1] = x; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ref-current-field-not-added-to-dep.js b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-field-not-added-to-dep.js new file mode 100644 index 0000000000..53d4489d8c --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-field-not-added-to-dep.js @@ -0,0 +1,8 @@ +function VideoTab() { + const ref = useRef(); + let x = () => { + ref.current.x; + }; + + return ; +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep-2.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep-2.expect.md new file mode 100644 index 0000000000..3edc16158e --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep-2.expect.md @@ -0,0 +1,45 @@ + +## Input + +```javascript +function Foo({ a }) { + const ref = useRef(); + const x = { a, val: ref.current }; + + return ; +} + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +function Foo(t18) { + const $ = useMemoCache(4); + const { a } = t18; + const ref = useRef(); + const c_0 = $[0] !== a; + let t0; + if (c_0) { + t0 = { a, val: ref.current }; + $[0] = a; + $[1] = t0; + } else { + t0 = $[1]; + } + const x = t0; + const c_2 = $[2] !== x; + let t1; + if (c_2) { + t1 = ; + $[2] = x; + $[3] = t1; + } else { + t1 = $[3]; + } + return t1; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep-2.js b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep-2.js new file mode 100644 index 0000000000..80ced49a9d --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep-2.js @@ -0,0 +1,6 @@ +function Foo({ a }) { + const ref = useRef(); + const x = { a, val: ref.current }; + + return ; +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep.expect.md new file mode 100644 index 0000000000..9f8e8a776d --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep.expect.md @@ -0,0 +1,46 @@ + +## Input + +```javascript +function VideoTab() { + const ref = useRef(); + let x = () => { + ref.current; + }; + + return ; +} + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +function VideoTab() { + const $ = useMemoCache(3); + const ref = useRef(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = () => { + ref.current; + }; + $[0] = t0; + } else { + t0 = $[0]; + } + const x = t0; + const c_1 = $[1] !== x; + let t1; + if (c_1) { + t1 = ; + $[1] = x; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep.js b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep.js new file mode 100644 index 0000000000..f3d6f574fe --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-not-added-to-dep.js @@ -0,0 +1,8 @@ +function VideoTab() { + const ref = useRef(); + let x = () => { + ref.current; + }; + + return ; +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ref-current-optional-field-no-added-to-dep.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-optional-field-no-added-to-dep.expect.md new file mode 100644 index 0000000000..6fe9a28cbb --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-optional-field-no-added-to-dep.expect.md @@ -0,0 +1,46 @@ + +## Input + +```javascript +function VideoTab() { + const ref = useRef(); + let x = () => { + ref.current?.x; + }; + + return ; +} + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +function VideoTab() { + const $ = useMemoCache(3); + const ref = useRef(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = () => { + ref.current?.x; + }; + $[0] = t0; + } else { + t0 = $[0]; + } + const x = t0; + const c_1 = $[1] !== x; + let t1; + if (c_1) { + t1 = ; + $[1] = x; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ref-current-optional-field-no-added-to-dep.js b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-optional-field-no-added-to-dep.js new file mode 100644 index 0000000000..92b26a8f9c --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-optional-field-no-added-to-dep.js @@ -0,0 +1,8 @@ +function VideoTab() { + const ref = useRef(); + let x = () => { + ref.current?.x; + }; + + return ; +} diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ref-current-write-not-added-to-dep.expect.md b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-write-not-added-to-dep.expect.md new file mode 100644 index 0000000000..6b6b95e201 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-write-not-added-to-dep.expect.md @@ -0,0 +1,46 @@ + +## Input + +```javascript +function VideoTab() { + const ref = useRef(); + let x = () => { + ref.current = 1; + }; + + return ; +} + +``` + +## Code + +```javascript +import { unstable_useMemoCache as useMemoCache } from "react"; +function VideoTab() { + const $ = useMemoCache(3); + const ref = useRef(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = () => { + ref.current = 1; + }; + $[0] = t0; + } else { + t0 = $[0]; + } + const x = t0; + const c_1 = $[1] !== x; + let t1; + if (c_1) { + t1 = ; + $[1] = x; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/compiler/ref-current-write-not-added-to-dep.js b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-write-not-added-to-dep.js new file mode 100644 index 0000000000..00b85bb682 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/compiler/ref-current-write-not-added-to-dep.js @@ -0,0 +1,8 @@ +function VideoTab() { + const ref = useRef(); + let x = () => { + ref.current = 1; + }; + + return ; +}