Repository: facebook/react
Pull request: [compiler] Support for useTransition

Stack from [ghstack](https://github.com/ezyang/ghstack) (oldest at bottom):
* __->__ #30681
* #30679

Summary:
UseTransition is a builtin hook that returns a stable value, like useState. This PR represents that in Forget, and marks the startTransition function as stable.

Changed files:
filename: compiler/packages/babel-plugin-react-compiler/src/HIR/Globals.ts
patch: @@ -18,6 +18,7 @@ import {
   BuiltInUseReducerId,
   BuiltInUseRefId,
   BuiltInUseStateId,
+  BuiltInUseTransitionId,
   ShapeRegistry,
   addFunction,
   addHook,
@@ -425,6 +426,17 @@ const REACT_APIS: Array<[string, BuiltInType]> = [
       BuiltInUseInsertionEffectHookId,
     ),
   ],
+  [
+    'useTransition',
+    addHook(DEFAULT_SHAPES, {
+      positionalParams: [],
+      restParam: null,
+      returnType: {kind: 'Object', shapeId: BuiltInUseTransitionId},
+      calleeEffect: Effect.Read,
+      hookKind: 'useTransition',
+      returnValueKind: ValueKind.Frozen,
+    }),
+  ],
   [
     'use',
     addFunction(

filename: compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
patch: @@ -1595,6 +1595,12 @@ export function isUseActionStateType(id: Identifier): boolean {
   );
 }
 
+export function isStartTransitionType(id: Identifier): boolean {
+  return (
+    id.type.kind === 'Function' && id.type.shapeId === 'BuiltInStartTransition'
+  );
+}
+
 export function isSetActionStateType(id: Identifier): boolean {
   return (
     id.type.kind === 'Function' && id.type.shapeId === 'BuiltInSetActionState'
@@ -1614,7 +1620,8 @@ export function isStableType(id: Identifier): boolean {
     isSetStateType(id) ||
     isSetActionStateType(id) ||
     isDispatcherType(id) ||
-    isUseRefType(id)
+    isUseRefType(id) ||
+    isStartTransitionType(id)
   );
 }
 

filename: compiler/packages/babel-plugin-react-compiler/src/HIR/ObjectShape.ts
patch: @@ -126,6 +126,7 @@ export type HookKind =
   | 'useInsertionEffect'
   | 'useMemo'
   | 'useCallback'
+  | 'useTransition'
   | 'Custom';
 
 /*
@@ -209,6 +210,8 @@ export const BuiltInUseOperatorId = 'BuiltInUseOperator';
 export const BuiltInUseReducerId = 'BuiltInUseReducer';
 export const BuiltInDispatchId = 'BuiltInDispatch';
 export const BuiltInUseContextHookId = 'BuiltInUseContextHook';
+export const BuiltInUseTransitionId = 'BuiltInUseTransition';
+export const BuiltInStartTransitionId = 'BuiltInStartTransition';
 
 // ShapeRegistry with default definitions for built-ins.
 export const BUILTIN_SHAPES: ShapeRegistry = new Map();
@@ -444,6 +447,25 @@ addObject(BUILTIN_SHAPES, BuiltInUseStateId, [
   ],
 ]);
 
+addObject(BUILTIN_SHAPES, BuiltInUseTransitionId, [
+  ['0', {kind: 'Primitive'}],
+  [
+    '1',
+    addFunction(
+      BUILTIN_SHAPES,
+      [],
+      {
+        positionalParams: [],
+        restParam: null,
+        returnType: PRIMITIVE_TYPE,
+        calleeEffect: Effect.Read,
+        returnValueKind: ValueKind.Primitive,
+      },
+      BuiltInStartTransitionId,
+    ),
+  ],
+]);
+
 addObject(BUILTIN_SHAPES, BuiltInUseActionStateId, [
   ['0', {kind: 'Poly'}],
   [

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/preserve-use-memo-transition.expect.md
patch: @@ -0,0 +1,52 @@
+
+## Input
+
+```javascript
+// @validatePreserveExistingMemoizationGuarantees
+import {useCallback, useTransition} from 'react';
+
+function useFoo() {
+  const [t, start] = useTransition();
+
+  return useCallback(() => {
+    start();
+  }, []);
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: useFoo,
+  params: [],
+};
+
+```
+
+## Code
+
+```javascript
+import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees
+import { useCallback, useTransition } from "react";
+
+function useFoo() {
+  const $ = _c(1);
+  const [t, start] = useTransition();
+  let t0;
+  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
+    t0 = () => {
+      start();
+    };
+    $[0] = t0;
+  } else {
+    t0 = $[0];
+  }
+  return t0;
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: useFoo,
+  params: [],
+};
+
+```
+      
+### Eval output
+(kind: ok) "[[ function params=0 ]]"
\ No newline at end of file

filename: compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/preserve-use-memo-transition.ts
patch: @@ -0,0 +1,15 @@
+// @validatePreserveExistingMemoizationGuarantees
+import {useCallback, useTransition} from 'react';
+
+function useFoo() {
+  const [t, start] = useTransition();
+
+  return useCallback(() => {
+    start();
+  }, []);
+}
+
+export const FIXTURE_ENTRYPOINT = {
+  fn: useFoo,
+  params: [],
+};