diff --git a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts index 1fa755499ed..dc00bf14925 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts @@ -147,8 +147,10 @@ function* runWithEnvironment( validateContextVariableLValues(hir); validateUseMemo(hir); - dropManualMemoization(hir); - yield log({ kind: "hir", name: "DropManualMemoization", value: hir }); + if (!env.config.enablePreserveExistingManualUseMemo) { + dropManualMemoization(hir); + yield log({ kind: "hir", name: "DropManualMemoization", value: hir }); + } inlineImmediatelyInvokedFunctionExpressions(hir); yield log({ diff --git a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts index f950068f15e..7375c35c765 100644 --- a/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts +++ b/compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts @@ -165,6 +165,13 @@ const EnvironmentConfigSchema = z.object({ */ validatePreserveExistingMemoizationGuarantees: z.boolean().default(true), + /** + * When this is true, rather than pruning existing manual memoization but ensuring or validating + * that the memoized values remain memoized, the compiler will simply not prune existing calls to + * useMemo/useCallback. + */ + enablePreserveExistingManualUseMemo: z.boolean().default(false), + // 🌲 enableForest: z.boolean().default(false), diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useMemo-simple-preserved.expect.md b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useMemo-simple-preserved.expect.md new file mode 100644 index 00000000000..6c813c27a68 --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useMemo-simple-preserved.expect.md @@ -0,0 +1,66 @@ + +## Input + +```javascript +// @enablePreserveExistingManualUseMemo +import { useMemo } from "react"; + +function Component({ a }) { + let x = useMemo(() => [a], []); + return
{x}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ a: 42 }], + isComponent: true, +}; + +``` + +## Code + +```javascript +import { c as _c } from "react/compiler-runtime"; // @enablePreserveExistingManualUseMemo +import { useMemo } from "react"; + +function Component(t0) { + const $ = _c(5); + const { a } = t0; + let t1; + if ($[0] !== a) { + t1 = () => [a]; + $[0] = a; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = []; + $[2] = t2; + } else { + t2 = $[2]; + } + const x = useMemo(t1, t2); + let t3; + if ($[3] !== x) { + t3 =
{x}
; + $[3] = x; + $[4] = t3; + } else { + t3 = $[4]; + } + return t3; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ a: 42 }], + isComponent: true, +}; + +``` + +### Eval output +(kind: ok)
42
\ No newline at end of file diff --git a/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useMemo-simple-preserved.js b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useMemo-simple-preserved.js new file mode 100644 index 00000000000..a5731f2f09d --- /dev/null +++ b/compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useMemo-simple-preserved.js @@ -0,0 +1,13 @@ +// @enablePreserveExistingManualUseMemo +import { useMemo } from "react"; + +function Component({ a }) { + let x = useMemo(() => [a], []); + return
{x}
; +} + +export const FIXTURE_ENTRYPOINT = { + fn: Component, + params: [{ a: 42 }], + isComponent: true, +};