Skip to content

Commit 03dbfa6

Browse files
committed
fix refresh behavior for discarded actions
1 parent bde8951 commit 03dbfa6

File tree

5 files changed

+78
-14
lines changed

5 files changed

+78
-14
lines changed

packages/next/src/client/components/layout-router.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -437,10 +437,10 @@ function InnerLayoutRouter({
437437
// It's important that we mark this as resolved, in case this branch is replayed, we don't want to continously re-apply
438438
// the patch to the tree.
439439
childNode.lazyDataResolved = true
440-
}
441440

442-
// Suspend infinitely as `changeByServerResponse` will cause a different part of the tree to be rendered.
443-
use(unresolvedThenable) as never
441+
// Suspend infinitely as `changeByServerResponse` will cause a different part of the tree to be rendered.
442+
use(unresolvedThenable) as never
443+
}
444444
}
445445

446446
// If we get to this point, then we know we have something we can render.

packages/next/src/shared/lib/router/action-queue.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ function runRemainingActions(
4848
action: actionQueue.pending,
4949
setState,
5050
})
51+
} else {
52+
// No more actions are pending, check if a refresh is needed
53+
if (actionQueue.needsRefresh) {
54+
actionQueue.needsRefresh = false
55+
actionQueue.dispatch(
56+
{
57+
type: ACTION_REFRESH,
58+
origin: window.location.origin,
59+
},
60+
setState
61+
)
62+
}
5163
}
5264
}
5365
}
@@ -75,17 +87,7 @@ async function runAction({
7587
function handleResult(nextState: AppRouterState) {
7688
// if we discarded this action, the state should also be discarded
7789
if (action.discarded) {
78-
// if a refresh is needed, we only want to trigger it once the action queue is empty
79-
if (actionQueue.needsRefresh && actionQueue.pending === null) {
80-
actionQueue.needsRefresh = false
81-
actionQueue.dispatch(
82-
{
83-
type: ACTION_REFRESH,
84-
origin: window.location.origin,
85-
},
86-
setState
87-
)
88-
}
90+
console.log('discarded an action')
8991
return
9092
}
9193

test/e2e/app-dir/actions/app-action.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,31 @@ createNextDescribe(
371371
}, 'success')
372372
})
373373

374+
it('should trigger a refresh for a server action that also dispatches a navigation event', async () => {
375+
let browser = await next.browser('/revalidate')
376+
let initialJustPutit = await browser.elementById('justputit').text()
377+
378+
// this triggers a revalidate + redirect in a client component
379+
await browser.elementById('redirect-revalidate-client').click()
380+
await retry(async () => {
381+
const newJustPutIt = await browser.elementById('justputit').text()
382+
expect(newJustPutIt).not.toBe(initialJustPutit)
383+
384+
expect(await browser.url()).toBe(`${next.url}/revalidate?foo=bar`)
385+
})
386+
387+
// this triggers a revalidate + redirect in a server component
388+
browser = await next.browser('/revalidate')
389+
initialJustPutit = await browser.elementById('justputit').text()
390+
await browser.elementById('redirect-revalidate').click()
391+
await retry(async () => {
392+
const newJustPutIt = await browser.elementById('justputit').text()
393+
expect(newJustPutIt).not.toBe(initialJustPutit)
394+
395+
expect(await browser.url()).toBe(`${next.url}/revalidate?foo=bar`)
396+
})
397+
})
398+
374399
it('should support next/dynamic with ssr: false', async () => {
375400
const browser = await next.browser('/dynamic-csr')
376401

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use client'
2+
3+
import { useRouter } from 'next/navigation'
4+
5+
export default function RedirectClientComponent({ action }) {
6+
const router = useRouter()
7+
return (
8+
<button
9+
id="redirect-revalidate-client"
10+
onClick={async () => {
11+
await action()
12+
router.push('/revalidate?foo=bar')
13+
}}
14+
>
15+
redirect + revalidate (client component)
16+
</button>
17+
)
18+
}

test/e2e/app-dir/actions/app/revalidate/page.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { redirect } from 'next/navigation'
77
import Link from 'next/link'
88

99
import { cookies } from 'next/headers'
10+
import RedirectClientComponent from './client'
1011

1112
export default async function Page() {
1213
const data = await fetch(
@@ -133,6 +134,24 @@ export default async function Page() {
133134
redirect
134135
</button>
135136
</form>
137+
<form>
138+
<button
139+
id="redirect-revalidate"
140+
formAction={async () => {
141+
'use server'
142+
revalidateTag('justputit')
143+
redirect('/revalidate?foo=bar')
144+
}}
145+
>
146+
redirect + revalidate
147+
</button>
148+
</form>
149+
<RedirectClientComponent
150+
action={async () => {
151+
'use server'
152+
revalidateTag('justputit')
153+
}}
154+
/>
136155
</>
137156
)
138157
}

0 commit comments

Comments
 (0)