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:
@@ -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) {
|
||||
|
||||
@@ -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 || {}),
|
||||
'**/*': [
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
// /////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
Reference in New Issue
Block a user