@samitouri / QOS-React-2 / commits / 4abe4b5821

[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

lauren committed Oct 29, 2024 at 21:36 UTC 4abe4b582106f2012f4eb9624a10f77d167cc848
3 files changed +185 -1
compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
+3 -1
@@ -852,7 +852,9 @@ export class Environment {
852 */
853 return (
854 this.#globals.get(binding.imported) ??
855 - (isHookName(binding.imported) ? this.#getCustomHookType() : null)
855 + (isHookName(binding.imported) || isHookName(binding.name)
856 + ? this.#getCustomHookType()
857 + : null)
858 );
859 } else {
860 const moduleType = this.#resolveModuleType(binding.module, loc);
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/import-as-local.expect.md new
+140
@@ -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
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/import-as-local.tsx new
+42
@@ -0,0 +1,42 @@
1 +import {
2 + useEffect,
3 + useRef,
4 + // @ts-expect-error
5 + experimental_useEffectEvent as useEffectEvent,
6 +} from 'react';
7 +
8 +let id = 0;
9 +function uniqueId() {
10 + 'use no memo';
11 + return id++;
12 +}
13 +
14 +export function useCustomHook(src: string): void {
15 + const uidRef = useRef(uniqueId());
16 + const destroyed = useRef(false);
17 + const getItem = (srcName, uid) => {
18 + return {srcName, uid};
19 + };
20 +
21 + const getItemEvent = useEffectEvent(() => {
22 + if (destroyed.current) return;
23 +
24 + getItem(src, uidRef.current);
25 + });
26 +
27 + useEffect(() => {
28 + destroyed.current = false;
29 + getItemEvent();
30 + }, []);
31 +}
32 +
33 +function Component() {
34 + useCustomHook('hello');
35 + return <div>Hello</div>;
36 +}
37 +
38 +export const FIXTURE_ENTRYPOINT = {
39 + fn: Component,
40 + isComponent: true,
41 + params: [{x: 1}],
42 +};