diff --git a/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state-separate-export/cached.ts b/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state-separate-export/cached.ts new file mode 100644 index 00000000000000..d56cefb2f11e29 --- /dev/null +++ b/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state-separate-export/cached.ts @@ -0,0 +1,7 @@ +'use cache' + +const getRandomValue = async () => { + return Math.random() +} + +export { getRandomValue } diff --git a/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state-separate-export/form.tsx b/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state-separate-export/form.tsx new file mode 100644 index 00000000000000..6cc3181d52832a --- /dev/null +++ b/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state-separate-export/form.tsx @@ -0,0 +1,15 @@ +'use client' + +import { useActionState } from 'react' +import { getRandomValue } from './cached' + +export function Form() { + const [result, formAction, isPending] = useActionState(getRandomValue, -1) + + return ( +
+ +

{isPending ? 'loading...' : result}

+
+ ) +} diff --git a/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state-separate-export/page.tsx b/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state-separate-export/page.tsx new file mode 100644 index 00000000000000..f88d511fb44458 --- /dev/null +++ b/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state-separate-export/page.tsx @@ -0,0 +1,5 @@ +import { Form } from './form' + +export default function Page() { + return
+} diff --git a/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state/cached.ts b/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state/cached.ts index 268266246f219f..be871502eb57f4 100644 --- a/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state/cached.ts +++ b/test/e2e/app-dir/use-cache/app/(partially-static)/use-action-state/cached.ts @@ -1,7 +1,5 @@ 'use cache' export async function getRandomValue() { - const v = Math.random() - console.log(v) - return v + return Math.random() } diff --git a/test/e2e/app-dir/use-cache/use-cache.test.ts b/test/e2e/app-dir/use-cache/use-cache.test.ts index 34cc2756e8cd81..9110bd3749d80e 100644 --- a/test/e2e/app-dir/use-cache/use-cache.test.ts +++ b/test/e2e/app-dir/use-cache/use-cache.test.ts @@ -499,6 +499,7 @@ describe('use-cache', () => { '/static-class-method', withCacheComponents && '/unhandled-promise-regression', '/use-action-state', + '/use-action-state-separate-export', '/with-server-action', ].filter(Boolean) ) @@ -709,6 +710,34 @@ describe('use-cache', () => { }) }) + // TODO: This test doesn't work currently because the compiler doesn't + // properly compute the server reference information byte that includes the + // function arity. Without this information, the client can't optimize the + // arguments it sends to the server, so the (unused) previous state is also + // sent as an argument, leading to cache misses. + it.failing( + 'works with useActionState if previousState parameter is not used in "use cache" function (separate export)', + async () => { + const browser = await next.browser('/use-action-state-separate-export') + + let value = await browser.elementByCss('p').text() + expect(value).toBe('-1') + + await browser.elementByCss('button').click() + + await retry(async () => { + value = await browser.elementByCss('p').text() + expect(value).toMatch(/\d\.\d+/) + }) + + await browser.elementByCss('button').click() + + await retry(async () => { + expect(await browser.elementByCss('p').text()).toBe(value) + }) + } + ) + it('works with "use cache" in method props', async () => { const browser = await next.browser('/method-props')