Skip to content

Instantly share code, notes, and snippets.

@HarleySalas
Created December 15, 2024 16:30
Show Gist options
  • Save HarleySalas/f059cfc8150074c779b806764b8fe890 to your computer and use it in GitHub Desktop.
Save HarleySalas/f059cfc8150074c779b806764b8fe890 to your computer and use it in GitHub Desktop.
Combine React's cache with Next's unstable_cache, with a simpler API
import { cache as reactCache } from 'react'
import { unstable_cache as nextCache } from 'next/cache'
type CacheOptions = {
revalidate?: number | false
tags?: string[] | ((...args: any[]) => string[])
}
/**
* Combines React's cache (for deduping requests) with Next.js unstable_cache (for persistent caching).
* Allows dynamic tags based on function arguments for targeted revalidation.
* @see https://nextjs.org/docs/app/api-reference/functions/unstable_cache#parameters
* @param fn - The async function to cache
* @param options - Cache configuration (revalidate duration and tags)
* @param closureVars - Only needed if fn uses external variables not passed as args
*/
export const cache = <TArgs extends any[], TResult>(
fn: (...args: TArgs) => Promise<TResult>,
options?: CacheOptions,
closureVars?: string[],
) =>
reactCache((...args: TArgs) =>
nextCache(fn, closureVars, {
revalidate: options?.revalidate,
tags: typeof options?.tags === 'function' ? options.tags(...args) : options?.tags,
})(...args),
)
@HarleySalas
Copy link
Author

HarleySalas commented Dec 15, 2024

Learn more about unstable_cache and React.Cache in NextJs

Usage examples

Simple case with static tags:

export const getSettings = cache(
  async () => {
    const payload = await getPayload({ config })
    const settings = await payload
      .findGlobal({ slug: 'settings' })
      .then((res) => res)
      .catch(() => null)
    return settings
  },
  { tags: [`settings`] },
)

Dynamic tags based on input

export const getPageByPathname = cache(
  async (pathname: string) => {
    const { isEnabled: draft } = await draftMode()
    const payload = await getPayload({ config })
    
    const { docs } = await payload.find({
      collection: 'page',
      draft,
      limit: 1,
      where: { pathname: { equals: pathname } }
    })
    return docs?.[0] || null
  },
  {
    tags: (pathname) => [pathname],
    revalidate: 3600
  }
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment