Skip to content

Commit d9b1c24

Browse files
committed
[compiler] Check if local identifier is a hook when resolving globals
When resolving import specifiers from the react namespace (`import {imported as local} from 'react'`), we were previously only checking if the `imported` identifier was a hook if we didn't already have its definition in the global registry. We also need to check if `local` is a hook in the case of aliasing since there may be hook-like APIs in react that don't start with `use` (eg they are experimental or unstable).
1 parent ebc5a37 commit d9b1c24

File tree

4 files changed

+143
-71
lines changed

4 files changed

+143
-71
lines changed

compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -852,7 +852,9 @@ export class Environment {
852852
*/
853853
return (
854854
this.#globals.get(binding.imported) ??
855-
(isHookName(binding.imported) ? this.#getCustomHookType() : null)
855+
(isHookName(binding.imported) || isHookName(binding.name)
856+
? this.#getCustomHookType()
857+
: null)
856858
);
857859
} else {
858860
const moduleType = this.#resolveModuleType(binding.module, loc);

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.bug-import-as-local.expect.md

Lines changed: 0 additions & 70 deletions
This file was deleted.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
2+
## Input
3+
4+
```javascript
5+
import {
6+
useEffect,
7+
useRef,
8+
// @ts-expect-error
9+
experimental_useEffectEvent as useEffectEvent,
10+
} from 'react';
11+
12+
let id = 0;
13+
function uniqueId() {
14+
'use no memo';
15+
return id++;
16+
}
17+
18+
export function useCustomHook(src: string): void {
19+
const uidRef = useRef(uniqueId());
20+
const destroyed = useRef(false);
21+
const getItem = (srcName, uid) => {
22+
return {srcName, uid};
23+
};
24+
25+
const getItemEvent = useEffectEvent(() => {
26+
if (destroyed.current) return;
27+
28+
getItem(src, uidRef.current);
29+
});
30+
31+
useEffect(() => {
32+
destroyed.current = false;
33+
getItemEvent();
34+
}, []);
35+
}
36+
37+
function Component() {
38+
useCustomHook('hello');
39+
return <div>Hello</div>;
40+
}
41+
42+
export const FIXTURE_ENTRYPOINT = {
43+
fn: Component,
44+
isComponent: true,
45+
params: [{x: 1}],
46+
};
47+
48+
```
49+
50+
## Code
51+
52+
```javascript
53+
import { c as _c } from "react/compiler-runtime";
54+
import {
55+
useEffect,
56+
useRef,
57+
// @ts-expect-error
58+
experimental_useEffectEvent as useEffectEvent,
59+
} from "react";
60+
61+
let id = 0;
62+
function uniqueId() {
63+
"use no memo";
64+
return id++;
65+
}
66+
67+
export function useCustomHook(src) {
68+
const $ = _c(6);
69+
let t0;
70+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
71+
t0 = uniqueId();
72+
$[0] = t0;
73+
} else {
74+
t0 = $[0];
75+
}
76+
const uidRef = useRef(t0);
77+
const destroyed = useRef(false);
78+
const getItem = _temp;
79+
let t1;
80+
if ($[1] !== src) {
81+
t1 = () => {
82+
if (destroyed.current) {
83+
return;
84+
}
85+
86+
getItem(src, uidRef.current);
87+
};
88+
$[1] = src;
89+
$[2] = t1;
90+
} else {
91+
t1 = $[2];
92+
}
93+
const getItemEvent = useEffectEvent(t1);
94+
let t2;
95+
if ($[3] !== getItemEvent) {
96+
t2 = () => {
97+
destroyed.current = false;
98+
getItemEvent();
99+
};
100+
$[3] = getItemEvent;
101+
$[4] = t2;
102+
} else {
103+
t2 = $[4];
104+
}
105+
let t3;
106+
if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
107+
t3 = [];
108+
$[5] = t3;
109+
} else {
110+
t3 = $[5];
111+
}
112+
useEffect(t2, t3);
113+
}
114+
function _temp(srcName, uid) {
115+
return { srcName, uid };
116+
}
117+
118+
function Component() {
119+
const $ = _c(1);
120+
useCustomHook("hello");
121+
let t0;
122+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
123+
t0 = <div>Hello</div>;
124+
$[0] = t0;
125+
} else {
126+
t0 = $[0];
127+
}
128+
return t0;
129+
}
130+
131+
export const FIXTURE_ENTRYPOINT = {
132+
fn: Component,
133+
isComponent: true,
134+
params: [{ x: 1 }],
135+
};
136+
137+
```
138+
139+
### Eval output
140+
(kind: exception) (0 , _react.experimental_useEffectEvent) is not a function

0 commit comments

Comments
 (0)