[compiler] Check if local identifier is a hook when resolving globals (#31384)

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).

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/31384).
* #31385
* __->__ #31384
* #31383
This commit is contained in:
lauren
2024-10-29 21:36:48 -04:00
committed by GitHub
parent 3928cb00db
commit 4abe4b5821
3 changed files with 185 additions and 1 deletions

View File

@@ -852,7 +852,9 @@ export class Environment {
*/
return (
this.#globals.get(binding.imported) ??
(isHookName(binding.imported) ? this.#getCustomHookType() : null)
(isHookName(binding.imported) || isHookName(binding.name)
? this.#getCustomHookType()
: null)
);
} else {
const moduleType = this.#resolveModuleType(binding.module, loc);

View File

@@ -0,0 +1,140 @@
## Input
```javascript
import {
useEffect,
useRef,
// @ts-expect-error
experimental_useEffectEvent as useEffectEvent,
} from 'react';
let id = 0;
function uniqueId() {
'use no memo';
return id++;
}
export function useCustomHook(src: string): void {
const uidRef = useRef(uniqueId());
const destroyed = useRef(false);
const getItem = (srcName, uid) => {
return {srcName, uid};
};
const getItemEvent = useEffectEvent(() => {
if (destroyed.current) return;
getItem(src, uidRef.current);
});
useEffect(() => {
destroyed.current = false;
getItemEvent();
}, []);
}
function Component() {
useCustomHook('hello');
return <div>Hello</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
isComponent: true,
params: [{x: 1}],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import {
useEffect,
useRef,
// @ts-expect-error
experimental_useEffectEvent as useEffectEvent,
} from "react";
let id = 0;
function uniqueId() {
"use no memo";
return id++;
}
export function useCustomHook(src) {
const $ = _c(6);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = uniqueId();
$[0] = t0;
} else {
t0 = $[0];
}
const uidRef = useRef(t0);
const destroyed = useRef(false);
const getItem = _temp;
let t1;
if ($[1] !== src) {
t1 = () => {
if (destroyed.current) {
return;
}
getItem(src, uidRef.current);
};
$[1] = src;
$[2] = t1;
} else {
t1 = $[2];
}
const getItemEvent = useEffectEvent(t1);
let t2;
if ($[3] !== getItemEvent) {
t2 = () => {
destroyed.current = false;
getItemEvent();
};
$[3] = getItemEvent;
$[4] = t2;
} else {
t2 = $[4];
}
let t3;
if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
t3 = [];
$[5] = t3;
} else {
t3 = $[5];
}
useEffect(t2, t3);
}
function _temp(srcName, uid) {
return { srcName, uid };
}
function Component() {
const $ = _c(1);
useCustomHook("hello");
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = <div>Hello</div>;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
isComponent: true,
params: [{ x: 1 }],
};
```
### Eval output
(kind: exception) (0 , _react.experimental_useEffectEvent) is not a function

View File

@@ -0,0 +1,42 @@
import {
useEffect,
useRef,
// @ts-expect-error
experimental_useEffectEvent as useEffectEvent,
} from 'react';
let id = 0;
function uniqueId() {
'use no memo';
return id++;
}
export function useCustomHook(src: string): void {
const uidRef = useRef(uniqueId());
const destroyed = useRef(false);
const getItem = (srcName, uid) => {
return {srcName, uid};
};
const getItemEvent = useEffectEvent(() => {
if (destroyed.current) return;
getItem(src, uidRef.current);
});
useEffect(() => {
destroyed.current = false;
getItemEvent();
}, []);
}
function Component() {
useCustomHook('hello');
return <div>Hello</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
isComponent: true,
params: [{x: 1}],
};