Skip to content

Commit

Permalink
feat(compiler): Consider dispatch fn from useActionState and useFormS…
Browse files Browse the repository at this point in the history
…tate to be non-reactive
  • Loading branch information
hieudo-dev committed Jun 4, 2024
1 parent 63d673c commit 941c01a
Show file tree
Hide file tree
Showing 9 changed files with 256 additions and 3 deletions.
26 changes: 26 additions & 0 deletions compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import { Effect, ValueKind, ValueReason } from "./HIR";
import {
BUILTIN_SHAPES,
BuiltInArrayId,
BuiltInUseActionStateId,
BuiltInUseEffectHookId,
BuiltInUseFormStateId,
BuiltInUseInsertionEffectHookId,
BuiltInUseLayoutEffectHookId,
BuiltInUseOperatorId,
Expand Down Expand Up @@ -265,6 +267,30 @@ const REACT_APIS: Array<[string, BuiltInType]> = [
returnValueReason: ValueReason.State,
}),
],
[
"useActionState",
addHook(DEFAULT_SHAPES, {
positionalParams: [],
restParam: Effect.Freeze,
returnType: { kind: "Object", shapeId: BuiltInUseActionStateId },
calleeEffect: Effect.Read,
hookKind: "useActionState",
returnValueKind: ValueKind.Frozen,
returnValueReason: ValueReason.State,
}),
],
[
"useFormState",
addHook(DEFAULT_SHAPES, {
positionalParams: [],
restParam: Effect.Freeze,
returnType: { kind: "Object", shapeId: BuiltInUseFormStateId },
calleeEffect: Effect.Read,
hookKind: "useFormState",
returnValueKind: ValueKind.Frozen,
returnValueReason: ValueReason.State,
}),
],
[
"useRef",
addHook(DEFAULT_SHAPES, {
Expand Down
20 changes: 20 additions & 0 deletions compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1490,6 +1490,26 @@ export function isSetStateType(id: Identifier): boolean {
return id.type.kind === "Function" && id.type.shapeId === "BuiltInSetState";
}

export function isUseActionStateType(id: Identifier): boolean {
return id.type.kind === "Object" && id.type.shapeId === 'BuiltInUseActionState'
}

export function isSetActionStateType(id: Identifier): boolean {
return id.type.kind === "Function" && id.type.shapeId === "BuiltInSetActionState";
}

export function isUseFormStateType(id: Identifier): boolean {
return id.type.kind === "Object" && id.type.shapeId === 'BuiltInUseFormState'
}

export function isSetFormStateType(id: Identifier): boolean {
return id.type.kind === "Function" && id.type.shapeId === "BuiltInSetFormState";
}

export function isDispatchType(id: Identifier): boolean {
return isSetActionStateType(id)|| isSetFormStateType(id);
}

export function isUseEffectHookType(id: Identifier): boolean {
return (
id.type.kind === "Function" && id.type.shapeId === "BuiltInUseEffectHook"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ function addShape(
export type HookKind =
| "useContext"
| "useState"
| "useActionState"
| "useFormState"
| "useRef"
| "useEffect"
| "useLayoutEffect"
Expand Down Expand Up @@ -193,6 +195,10 @@ export const BuiltInJsxId = "BuiltInJsx";
export const BuiltInObjectId = "BuiltInObject";
export const BuiltInUseStateId = "BuiltInUseState";
export const BuiltInSetStateId = "BuiltInSetState";
export const BuiltInUseActionStateId = "BuiltInUseActionState";
export const BuiltInSetActionStateId = "BuiltInSetActionState";
export const BuiltInUseFormStateId = "BuiltInUseFormState";
export const BuiltInSetFormStateId = "BuiltInSetFormState";
export const BuiltInUseRefId = "BuiltInUseRefId";
export const BuiltInRefValueId = "BuiltInRefValue";
export const BuiltInMixedReadonlyId = "BuiltInMixedReadonly";
Expand Down Expand Up @@ -387,6 +393,44 @@ addObject(BUILTIN_SHAPES, BuiltInUseStateId, [
],
]);

addObject(BUILTIN_SHAPES, BuiltInUseActionStateId, [
["0", { kind: "Poly" }],
[
"1",
addFunction(
BUILTIN_SHAPES,
[],
{
positionalParams: [],
restParam: Effect.Freeze,
returnType: PRIMITIVE_TYPE,
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
},
BuiltInSetActionStateId
),
],
]);

addObject(BUILTIN_SHAPES, BuiltInUseFormStateId, [
["0", { kind: "Poly" }],
[
"1",
addFunction(
BUILTIN_SHAPES,
[],
{
positionalParams: [],
restParam: Effect.Freeze,
returnType: PRIMITIVE_TYPE,
calleeEffect: Effect.Read,
returnValueKind: ValueKind.Primitive,
},
BuiltInSetFormStateId
),
],
]);

addObject(BUILTIN_SHAPES, BuiltInUseRefId, [
["current", { kind: "Object", shapeId: BuiltInRefValueId }],
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
Place,
computePostDominatorTree,
getHookKind,
isDispatchType,
isSetStateType,
isUseOperator,
} from "../HIR";
Expand Down Expand Up @@ -219,7 +220,10 @@ export function inferReactivePlaces(fn: HIRFunction): void {

if (hasReactiveInput) {
for (const lvalue of eachInstructionLValue(instruction)) {
if (isSetStateType(lvalue.identifier)) {
if (
isSetStateType(lvalue.identifier) ||
isDispatchType(lvalue.identifier)
) {
continue;
}
reactiveIdentifiers.markReactive(lvalue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ReactiveFunction,
ReactiveInstruction,
ReactiveScopeBlock,
isDispatchType,
isSetStateType,
} from "../HIR";
import { eachPatternOperand } from "../HIR/visitors";
Expand Down Expand Up @@ -56,7 +57,7 @@ class Visitor extends ReactiveFunctionVisitor<ReactiveIdentifiers> {
case "Destructure": {
if (state.has(value.value.identifier.id)) {
for (const lvalue of eachPatternOperand(value.lvalue.pattern)) {
if (isSetStateType(lvalue.identifier)) {
if (isSetStateType(lvalue.identifier) || isDispatchType(lvalue.identifier)) {
continue;
}
state.add(lvalue.identifier.id);
Expand All @@ -71,7 +72,7 @@ class Visitor extends ReactiveFunctionVisitor<ReactiveIdentifiers> {
if (
lvalue !== null &&
state.has(value.object.identifier.id) &&
!isSetStateType(lvalue.identifier)
!isSetStateType(lvalue.identifier) && !isDispatchType(lvalue.identifier)
) {
state.add(lvalue.identifier.id);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

## Input

```javascript
import { useActionState } from "react";

function Component() {
const [actionState, dispatchAction] = useActionState();
const onSubmitAction = () => {
dispatchAction();
};
return <Foo onSubmitAction={onSubmitAction} />;
}

function Foo() {}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime";
import { useActionState } from "react";

function Component() {
const $ = _c(2);
const [actionState, dispatchAction] = useActionState();
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = () => {
dispatchAction();
};
$[0] = t0;
} else {
t0 = $[0];
}
const onSubmitAction = t0;
let t1;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
t1 = <Foo onSubmitAction={onSubmitAction} />;
$[1] = t1;
} else {
t1 = $[1];
}
return t1;
}

function Foo() {}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};

```
### Eval output
(kind: ok)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useActionState } from "react";

function Component() {
const [actionState, dispatchAction] = useActionState();
const onSubmitAction = () => {
dispatchAction();
};
return <Foo onSubmitAction={onSubmitAction} />;
}

function Foo() {}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

## Input

```javascript
import { useFormState } from "react";

function Component() {
const [formState, dispatchForm] = useFormState();
const onSubmitForm = () => {
dispatchForm();
};
return <Foo onSubmitForm={onSubmitForm} />;
}

function Foo() {}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};

```

## Code

```javascript
import { c as _c } from "react/compiler-runtime";
import { useFormState } from "react";

function Component() {
const $ = _c(2);
const [formState, dispatchForm] = useFormState();
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = () => {
dispatchForm();
};
$[0] = t0;
} else {
t0 = $[0];
}
const onSubmitForm = t0;
let t1;
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
t1 = <Foo onSubmitForm={onSubmitForm} />;
$[1] = t1;
} else {
t1 = $[1];
}
return t1;
}

function Foo() {}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};

```
### Eval output
(kind: exception) (0 , _react.useFormState) is not a function or its return value is not iterable
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useFormState } from "react";

function Component() {
const [formState, dispatchForm] = useFormState();
const onSubmitForm = () => {
dispatchForm();
};
return <Foo onSubmitForm={onSubmitForm} />;
}

function Foo() {}

export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [],
};

0 comments on commit 941c01a

Please sign in to comment.