perf: disable router cache refresh that occurs after every page transition or page load (#8318)

This speeds up all page loads and reduces the amount of requests

## Example

### Clientside transition from dashboard => ui-fields list view

#### Router cache disabled
 GET /admin/collections/ui-fields 200 in 33ms
 POST /api/form-state 200 in 9ms
 POST /api/form-state 200 in 10ms
 GET /api/payload-preferences/ui-fields-list 200 in 11ms
 GET /admin/collections/ui-fields?limit=10&sort=id 200 in 42ms
 
#### Router cache enabled
 GET /admin/collections/ui-fields 200 in 33ms
 POST /api/form-state 200 in 11ms
 POST /api/form-state 200 in 12ms
 GET /api/payload-preferences/ui-fields-list 200 in 15ms
 GET /admin/collections/ui-fields?limit=10&sort=id 200 in 42ms
**GET /admin/collections/ui-fields?limit=10&sort=id 200 in 82ms** <==
this is gone
This commit is contained in:
Alessio Gravili
2024-09-20 09:57:09 -04:00
committed by GitHub
parent 63b446c82b
commit 0cdd5b628c
11 changed files with 33 additions and 23 deletions

View File

@@ -28,13 +28,13 @@ import {
useListInfo,
useListQuery,
useModal,
useRouteCache,
useStepNav,
useTranslation,
useWindowInfo,
ViewDescription,
} from '@payloadcms/ui'
import LinkImport from 'next/link.js'
import { useRouter } from 'next/navigation.js'
import { formatFilesize, isNumber } from 'payload/shared'
import React, { Fragment, useEffect } from 'react'
@@ -55,9 +55,10 @@ export const DefaultListView: React.FC = () => {
newDocumentURL,
} = useListInfo()
const router = useRouter()
const { data, defaultLimit, handlePageChange, handlePerPageChange, params } = useListQuery()
const { openModal } = useModal()
const { clearRouteCache } = useRouteCache()
const { setCollectionSlug, setOnSuccess } = useBulkUpload()
const { drawerSlug } = useBulkUpload()
@@ -109,8 +110,8 @@ export const DefaultListView: React.FC = () => {
const openBulkUpload = React.useCallback(() => {
setCollectionSlug(collectionSlug)
openModal(drawerSlug)
setOnSuccess(clearRouteCache)
}, [clearRouteCache, collectionSlug, drawerSlug, openModal, setCollectionSlug, setOnSuccess])
setOnSuccess(() => router.refresh())
}, [router, collectionSlug, drawerSlug, openModal, setCollectionSlug, setOnSuccess])
useEffect(() => {
if (drawerDepth <= 1) {

View File

@@ -4,10 +4,13 @@
* @returns {import('next').NextConfig}
* */
export const withPayload = (nextConfig = {}) => {
const env = nextConfig?.env || {}
if (nextConfig.experimental?.staleTimes?.dynamic) {
console.warn(
'Payload detected a non-zero value for the `staleTimes.dynamic` option in your Next.js config. This may cause stale data to load in the Admin Panel. To clear this warning, remove the `staleTimes.dynamic` option from your Next.js config or set it to 0. In the future, Next.js may support scoping this option to specific routes.',
'Payload detected a non-zero value for the `staleTimes.dynamic` option in your Next.js config. This will slow down page transitions and may cause stale data to load within the Admin panel. To clear this warning, remove the `staleTimes.dynamic` option from your Next.js config or set it to 0. In the future, Next.js may support scoping this option to specific routes.',
)
env.NEXT_PUBLIC_ENABLE_ROUTER_CACHE_REFRESH = 'true'
}
/**
@@ -15,9 +18,7 @@ export const withPayload = (nextConfig = {}) => {
*/
const toReturn = {
...nextConfig,
env: {
...(nextConfig?.env || {}),
},
env,
outputFileTracingExcludes: {
...(nextConfig?.outputFileTracingExcludes || {}),
'**/*': [

View File

@@ -173,6 +173,7 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
req,
where: whereConstraint,
})
user.collection = collectionConfig.slug
if (!user || (args.collection.config.auth.verify && user._verified === false)) {
throw new AuthenticationError(req.t, Boolean(canLoginWithUsername && sanitizedUsername))

View File

@@ -37,6 +37,7 @@ export const meOperation = async (args: Arguments): Promise<MeOperationResult> =
req,
showHiddenFields: false,
})) as User
user.collection = collection.config.slug
if (req.user.collection !== collection.config.slug) {
return {
@@ -44,8 +45,6 @@ export const meOperation = async (args: Arguments): Promise<MeOperationResult> =
}
}
delete user.collection
// /////////////////////////////////////
// me hook - Collection
// /////////////////////////////////////

View File

@@ -75,6 +75,7 @@ export const refreshOperation = async (incomingArgs: Arguments): Promise<Result>
depth: isGraphQL ? 0 : args.collection.config.auth.depth,
req: args.req,
})
user.collection = args.req.user.collection
let result: Result

View File

@@ -61,19 +61,24 @@ export type Permissions = {
}
}
export type User = {
[key: string]: any // This NEEDS to be an any, otherwise it breaks the Omit for ClientUser below
type BaseUser = {
collection: string
email?: string
id: number | string
username?: string
}
export type User = {
[key: string]: any
} & BaseUser
/**
* `collection` is not available one the client. It's only available on the server (req.user)
* On the client, you can access the collection via config.admin.user. Config can be accessed using the useConfig() hook
*/
export type ClientUser = Omit<User, 'collection'>
export type ClientUser = {
[key: string]: any
} & BaseUser
type GenerateVerifyEmailHTML<TUser = any> = (args: {
req: PayloadRequest

View File

@@ -173,7 +173,7 @@ export const EditMany: React.FC<EditManyProps> = (props) => {
params: { page: selectAll === SelectAllStatus.AllAvailable ? '1' : undefined },
}),
)
clearRouteCache()
clearRouteCache() // Use clearRouteCache instead of router.refresh, as we only need to clear the cache if the user has route caching enabled - clearRouteCache checks for this
closeModal(drawerSlug)
}

View File

@@ -81,7 +81,7 @@ export const PublishMany: React.FC<PublishManyProps> = (props) => {
}),
)
clearRouteCache()
clearRouteCache() // Use clearRouteCache instead of router.refresh, as we only need to clear the cache if the user has route caching enabled - clearRouteCache checks for this
return null
}

View File

@@ -78,7 +78,7 @@ export const UnpublishMany: React.FC<UnpublishManyProps> = (props) => {
},
}),
)
clearRouteCache()
clearRouteCache() // Use clearRouteCache instead of router.refresh, as we only need to clear the cache if the user has route caching enabled - clearRouteCache checks for this
return null
}

View File

@@ -58,9 +58,12 @@ export const RootProvider: React.FC<Props> = ({
translations,
user,
}) => {
const RouteCacheComponent =
process.env.NEXT_PUBLIC_ENABLE_ROUTER_CACHE_REFRESH === 'true' ? RouteCache : Fragment
return (
<Fragment>
<RouteCache>
<RouteCacheComponent>
<ConfigProvider config={config}>
<FieldComponentsProvider fieldComponents={fieldComponents}>
<ClientFunctionProvider>
@@ -114,7 +117,7 @@ export const RootProvider: React.FC<Props> = ({
</ClientFunctionProvider>
</FieldComponentsProvider>
</ConfigProvider>
</RouteCache>
</RouteCacheComponent>
<ToastContainer />
</Fragment>
)

View File

@@ -12,10 +12,9 @@ import type { ClientConfig, LanguageOptions } from 'payload'
import { importDateFNSLocale, t } from '@payloadcms/translations'
import { enUS } from 'date-fns/locale/en-US'
import { useRouter } from 'next/navigation.js'
import React, { createContext, useContext, useEffect, useState } from 'react'
import { useRouteCache } from '../RouteCache/index.js'
type ContextType<
TAdditionalTranslations = {},
TAdditionalClientTranslationKeys extends string = never,
@@ -64,7 +63,7 @@ export const TranslationProvider: React.FC<Props> = ({
switchLanguageServerAction,
translations,
}) => {
const { clearRouteCache } = useRouteCache()
const router = useRouter()
const [dateFNS, setDateFNS] = useState<Locale>()
const nextT: ContextType['t'] = (key, vars): string =>
@@ -78,13 +77,13 @@ export const TranslationProvider: React.FC<Props> = ({
async (lang: string) => {
try {
await switchLanguageServerAction(lang)
clearRouteCache()
router.refresh()
} catch (error) {
// eslint-disable-next-line no-console
console.error(`Error loading language: "${lang}"`, error)
}
},
[switchLanguageServerAction, clearRouteCache],
[switchLanguageServerAction, router],
)
useEffect(() => {