Skip to content

Commit

Permalink
refresh + test
Browse files Browse the repository at this point in the history
  • Loading branch information
ztanner committed Oct 20, 2023
1 parent 2966f85 commit 00660cf
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 3 deletions.
2 changes: 1 addition & 1 deletion packages/next/src/client/components/app-router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ function HistoryUpdater({ tree, pushRef, canonicalUrl, sync }: any) {
return null
}

const createEmptyCacheNode = () => ({
export const createEmptyCacheNode = () => ({
status: CacheStates.LAZY_INITIALIZED,
data: null,
subTreeData: null,
Expand Down
27 changes: 25 additions & 2 deletions packages/next/src/shared/lib/router/action-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import {
type AppRouterState,
type ReducerActions,
type ReducerState,
ACTION_REFRESH,
ACTION_SERVER_ACTION,
ACTION_NAVIGATE,
} from '../../../client/components/router-reducer/router-reducer-types'
import type { ReduxDevToolsInstance } from '../../../client/components/use-reducer-with-devtools'
import { reducer } from '../../../client/components/router-reducer/router-reducer'
import React, { startTransition } from 'react'
import { createEmptyCacheNode } from '../../../client/components/app-router'

export type DispatchStatePromise = React.Dispatch<ReducerState>

Expand All @@ -16,6 +20,7 @@ export type AppRouterActionQueue = {
dispatch: (payload: ReducerActions, setState: DispatchStatePromise) => void
action: (state: AppRouterState, action: ReducerActions) => ReducerState
pending: ActionQueueNode | null
needsRefresh?: boolean
last: ActionQueueNode | null
}

Expand Down Expand Up @@ -70,7 +75,21 @@ async function runAction({

function handleResult(nextState: AppRouterState) {
// if we discarded this action, the state should also be discarded
if (action.discarded) return
if (action.discarded) {
if (actionQueue.needsRefresh) {
actionQueue.needsRefresh = false
actionQueue.dispatch(
{
type: ACTION_REFRESH,
cache: createEmptyCacheNode(),
mutable: {},
origin: window.location.origin,
},
setState
)
}
return
}

actionQueue.state = nextState

Expand Down Expand Up @@ -121,11 +140,15 @@ function dispatchAction(
setState(deferredPromise)
})

if (payload.type === 'navigate' && actionQueue.pending !== null) {
if (payload.type === ACTION_NAVIGATE && actionQueue.pending !== null) {
// Navigations take priority over any pending actions.
// Mark the pending action as discarded (so the state is never applied) and start the navigation action immediately.
actionQueue.pending.discarded = true

// if the pending action was a server action, mark the queue as needing a refresh once events are processed
actionQueue.needsRefresh =
actionQueue.pending.payload.type === ACTION_SERVER_ACTION

runAction({
actionQueue,
action: newAction,
Expand Down
21 changes: 21 additions & 0 deletions test/e2e/app-dir/actions/app-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,27 @@ createNextDescribe(
await check(() => browser.url(), `${next.url}/client`, true, 2)
})

it('should trigger a refresh for a server action that gets discarded due to a navigation', async () => {
let browser = await next.browser('/client')
const initialRandomNumber = await browser
.elementByCss('#random-number')
.text()

await browser.elementByCss('#slow-inc').click()

// navigate to server
await browser.elementByCss('#navigate-server').click()

// wait for the action to be completed
await check(async () => {
const newRandomNumber = await browser
.elementByCss('#random-number')
.text()

return newRandomNumber === initialRandomNumber ? 'fail' : 'success'
}, 'success')
})

it('should support next/dynamic with ssr: false', async () => {
const browser = await next.browser('/dynamic-csr')

Expand Down

0 comments on commit 00660cf

Please sign in to comment.