[entrypoint] Allow ref params to component functions
Mofei Zhang committed
Jan 19, 2024 at 14:59 UTC
34c89458f2db439c24e7f5e6c762ca75066b4787
3 files changed
+77
-7
compiler/packages/babel-plugin-react-forget/src/Entrypoint/Program.ts
+23
-7
@@ -481,6 +481,28 @@ function isMemoCallback(path: NodePath<t.Expression>): boolean {
481
);
482
}
483
484
+function isValidComponentParams(
485
+ params: Array<NodePath<t.Identifier | t.Pattern | t.RestElement>>
486
+): boolean {
487
+ if (params.length === 0) {
488
+ return true;
489
+ } else if (params.length === 1) {
490
+ return !params[0].isRestElement();
491
+ } else if (params.length === 2) {
492
+ // check if second param might be a ref
493
+ if (params[1].isIdentifier()) {
494
+ const { name } = params[1].node;
495
+ return name.includes("ref") || name.includes("Ref");
496
+ }
497
+ /**
498
+ * Otherwise, avoid helper functions that take more than one argument.
499
+ * Helpers are _usually_ named with lowercase, but some code may
500
+ * violate this rule
501
+ */
502
+ }
503
+ return false;
504
+}
505
+
506
/*
507
* Adapted from the ESLint rule at
508
* https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js#L90-L103
@@ -495,13 +517,7 @@ function isComponentOrHookLike(
517
if (functionName !== null && isComponentName(functionName)) {
518
return (
519
// As an added check we also look for hook invocations or JSX
498
- callsHooksOrCreatesJsx(node) &&
499
- /*
500
- * and avoid helper functions that take more than one argument
501
- * helpers are _usually_ named with lowercase, but some code may
502
- * violate this rule
503
- */
504
- node.get("params").length <= 1
520
+ callsHooksOrCreatesJsx(node) && isValidComponentParams(node.get("params"))
521
);
522
} else if (functionName !== null && isHook(functionName)) {
523
// Hooks have hook invocations or JSX, but can take any # of arguments
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/infer-functions-component-with-ref-arg.expect.md
new
+44
@@ -0,0 +1,44 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+// @compilationMode(infer)
6
+
7
+function Foo({}, ref) {
8
+ return <div ref={ref} />;
9
+}
10
+
11
+export const FIXTURE_ENTRYPOINT = {
12
+ fn: Foo,
13
+ params: [{}],
14
+};
15
+
16
+```
17
+
18
+## Code
19
+
20
+```javascript
21
+import { unstable_useMemoCache as useMemoCache } from "react"; // @compilationMode(infer)
22
+
23
+function Foo(t6, ref) {
24
+ const $ = useMemoCache(2);
25
+ let t0;
26
+ if ($[0] !== ref) {
27
+ t0 = <div ref={ref} />;
28
+ $[0] = ref;
29
+ $[1] = t0;
30
+ } else {
31
+ t0 = $[1];
32
+ }
33
+ return t0;
34
+}
35
+
36
+export const FIXTURE_ENTRYPOINT = {
37
+ fn: Foo,
38
+ params: [{}],
39
+};
40
+
41
+```
42
+
43
+### Eval output
44
+(kind: ok) <div></div>
\ No newline at end of file
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/infer-functions-component-with-ref-arg.js
new
+10
@@ -0,0 +1,10 @@
1
+// @compilationMode(infer)
2
+
3
+function Foo({}, ref) {
4
+ return <div ref={ref} />;
5
+}
6
+
7
+export const FIXTURE_ENTRYPOINT = {
8
+ fn: Foo,
9
+ params: [{}],
10
+};