Skip to content

Commit

Permalink
provide revalidateReason to getStaticProps (#64258)
Browse files Browse the repository at this point in the history
Provides a `revalidateReason` argument to `getStaticProps` ("stale" |
"on-demand" | "build").

- Build indicates it was run at build time
- On-demand indicates it was run as a side effect of [on-demand
revalidation](https://nextjs.org/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation)
- Stale indicates the resource was considered stale (either due to being
in dev mode, or an expired revalidate period)

This will allow changing behavior based on the context in which it's
called.

Closes NEXT-1900
  • Loading branch information
ztanner committed Apr 9, 2024
1 parent 39a1c2a commit 85b9ed5
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export default function Page({ repo }) {

> Note that irrespective of rendering type, any `props` will be passed to the page component and can be viewed on the client-side in the initial HTML. This is to allow the page to be [hydrated](https://react.dev/reference/react-dom/hydrate) correctly. Make sure that you don't pass any sensitive information that shouldn't be available on the client in `props`.
The [`getStaticProps` API reference](/docs/pages/api-reference/functions/get-static-props) covers all parameters and props that can be used with `getStaticProps`.

## When should I use getStaticProps?

You should use `getStaticProps` if:
Expand Down
19 changes: 10 additions & 9 deletions docs/03-pages/02-api-reference/02-functions/get-static-props.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,16 @@ You can import modules in top-level scope for use in `getStaticProps`. Imports u

The `context` parameter is an object containing the following keys:

| Name | Description |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `params` | Contains the route parameters for pages using [dynamic routes](/docs/pages/building-your-application/routing/dynamic-routes). For example, if the page name is `[id].js`, then `params` will look like `{ id: ... }`. You should use this together with `getStaticPaths`, which we'll explain later. |
| `preview` | (Deprecated for `draftMode`) `preview` is `true` if the page is in the [Preview Mode](/docs/pages/building-your-application/configuring/preview-mode) and `false` otherwise. |
| `previewData` | (Deprecated for `draftMode`) The [preview](/docs/pages/building-your-application/configuring/preview-mode) data set by `setPreviewData`. |
| `draftMode` | `draftMode` is `true` if the page is in the [Draft Mode](/docs/pages/building-your-application/configuring/draft-mode) and `false` otherwise. |
| `locale` | Contains the active locale (if enabled). |
| `locales` | Contains all supported locales (if enabled). |
| `defaultLocale` | Contains the configured default locale (if enabled). |
| Name | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `params` | Contains the route parameters for pages using [dynamic routes](/docs/pages/building-your-application/routing/dynamic-routes). For example, if the page name is `[id].js`, then `params` will look like `{ id: ... }`. You should use this together with `getStaticPaths`, which we'll explain later. |
| `preview` | (Deprecated for `draftMode`) `preview` is `true` if the page is in the [Preview Mode](/docs/pages/building-your-application/configuring/preview-mode) and `false` otherwise. |
| `previewData` | (Deprecated for `draftMode`) The [preview](/docs/pages/building-your-application/configuring/preview-mode) data set by `setPreviewData`. |
| `draftMode` | `draftMode` is `true` if the page is in the [Draft Mode](/docs/pages/building-your-application/configuring/draft-mode) and `false` otherwise. |
| `locale` | Contains the active locale (if enabled). |
| `locales` | Contains all supported locales (if enabled). |
| `defaultLocale` | Contains the configured default locale (if enabled). |
| `revalidateReason` | Provides a reason for why the function was called. Can be one of: "build" (run at build time), "stale" (revalidate period expired, or running in [development mode](/docs/pages/building-your-application/data-fetching/get-static-props#runs-on-every-request-in-development)), "on-demand" (triggered via [on-demand revalidation](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation)) |

## getStaticProps return values

Expand Down
7 changes: 6 additions & 1 deletion packages/next/src/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ export async function renderToHTMLImpl(
},
},
() =>
getStaticProps!({
getStaticProps({
...(pageIsDynamic
? { params: query as ParsedUrlQuery }
: undefined),
Expand All @@ -855,6 +855,11 @@ export async function renderToHTMLImpl(
locales: renderOpts.locales,
locale: renderOpts.locale,
defaultLocale: renderOpts.defaultLocale,
revalidateReason: renderOpts.isOnDemandRevalidate
? 'on-demand'
: isBuildTimeSSG
? 'build'
: 'stale',
})
)
} catch (staticPropsError: any) {
Expand Down
1 change: 1 addition & 0 deletions packages/next/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export type GetStaticPropsContext<
locale?: string
locales?: string[]
defaultLocale?: string
revalidateReason?: 'on-demand' | 'build' | 'stale'
}

/**
Expand Down
15 changes: 15 additions & 0 deletions test/e2e/revalidate-reason/pages/api/revalidate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { NextApiRequest, NextApiResponse } from 'next'

export default async function handler(
req: NextApiRequest,
res: NextApiResponse<{ revalidated: boolean }>
) {
try {
await res.revalidate('/')
return res.json({ revalidated: true })
} catch (err) {
console.error('Failed to revalidate:', err)
}

res.json({ revalidated: false })
}
17 changes: 17 additions & 0 deletions test/e2e/revalidate-reason/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react'

export async function getStaticProps({ params, revalidateReason }) {
console.log(
'revalidate-reason/pages/index.tsx revalidateReason:',
revalidateReason
)
return {
props: {
hello: 'world',
},
}
}

export default ({ hello }) => {
return <p>hello: {hello}</p>
}
18 changes: 18 additions & 0 deletions test/e2e/revalidate-reason/pages/stale.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react'

export async function getStaticProps({ params, revalidateReason }) {
console.log(
'revalidate-reason/pages/stale.tsx revalidateReason:',
revalidateReason
)
return {
props: {
hello: 'world',
},
revalidate: 5,
}
}

export default ({ hello }) => {
return <p>hello: {hello}</p>
}
61 changes: 61 additions & 0 deletions test/e2e/revalidate-reason/revalidate-reason.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { nextTestSetup } from 'e2e-utils'
import { retry, waitFor } from 'next-test-utils'

describe('revalidate-reason', () => {
const { next, isNextDev } = nextTestSetup({
files: __dirname,
})

if (isNextDev) {
describe('development mode', () => {
// in dev mode, the revalidateReason is called on every request, and will always be considered stale
it('should support revalidateReason: "stale"', async () => {
const res = await next.fetch('/')

expect(res.status).toBe(200)

expect(next.cliOutput).toContain(
'revalidate-reason/pages/index.tsx revalidateReason: stale'
)
})
})

// skip the remaining tests as they do not apply in dev
return
}

it('should support revalidateReason: "build"', async () => {
expect(next.cliOutput).toContain(
'revalidate-reason/pages/index.tsx revalidateReason: build'
)
expect(next.cliOutput).toContain(
'revalidate-reason/pages/stale.tsx revalidateReason: build'
)
})

it('should support revalidateReason: "on-demand"', async () => {
const res = await next.fetch('/api/revalidate')

expect(res.status).toBe(200)

expect(next.cliOutput).toContain(
'revalidate-reason/pages/index.tsx revalidateReason: on-demand'
)
})

it('should support revalidateReason: "stale"', async () => {
const res = await next.fetch('/stale')
expect(res.status).toBe(200)

// wait for the revalidation period
await waitFor(5000)

await retry(async () => {
await next.fetch('/stale')

expect(next.cliOutput).toContain(
'revalidate-reason/pages/stale.tsx revalidateReason: stale'
)
})
})
})

0 comments on commit 85b9ed5

Please sign in to comment.