The biggest difference comes from calling `RenderServerComponent` as a function, instead of rendering it by using `<RenderServerComponent`. This gets rid of wasteful blocks of codes sent to the client that look like this:  HTML size comparison: ## Admin test suite | View | Before | After | |------|---------|--------| | Dashboard | 331 kB | 83 kB | | collections/custom-views-one Edit | 285 kB | 76.6 kB | ## Fields test suite | View | Before | After | |------|---------|--------| | collections/lexical Edit | 189 kB | 94.4 kB | | collections/lexical List | 152 kB | 62.9 kB | ## Community test suite | View | Before | After | |------|---------|--------| | Dashboard | 78.9 kB | 43.1 kB |
90 lines
2.3 KiB
TypeScript
90 lines
2.3 KiB
TypeScript
import type { ImportMap, PayloadComponent } from 'payload'
|
|
|
|
import { getFromImportMap, isPlainObject, isReactServerComponentOrFunction } from 'payload/shared'
|
|
import React from 'react'
|
|
|
|
import { removeUndefined } from '../../utilities/removeUndefined.js'
|
|
|
|
type RenderServerComponentFn = (args: {
|
|
readonly clientProps?: object
|
|
readonly Component?:
|
|
| PayloadComponent
|
|
| PayloadComponent[]
|
|
| React.ComponentType
|
|
| React.ComponentType[]
|
|
readonly Fallback?: React.ComponentType
|
|
readonly importMap: ImportMap
|
|
readonly key?: string
|
|
readonly serverProps?: object
|
|
}) => React.ReactNode
|
|
|
|
/**
|
|
* Can be used to render both MappedComponents and React Components.
|
|
*/
|
|
export const RenderServerComponent: RenderServerComponentFn = ({
|
|
clientProps = {},
|
|
Component,
|
|
Fallback,
|
|
importMap,
|
|
key,
|
|
serverProps,
|
|
}) => {
|
|
if (Array.isArray(Component)) {
|
|
return Component.map((c, index) =>
|
|
RenderServerComponent({
|
|
clientProps,
|
|
Component: c,
|
|
importMap,
|
|
key: index,
|
|
serverProps,
|
|
}),
|
|
)
|
|
}
|
|
|
|
if (typeof Component === 'function') {
|
|
const isRSC = isReactServerComponentOrFunction(Component)
|
|
|
|
// prevent $undefined from being passed through the rsc requests
|
|
const sanitizedProps = removeUndefined({
|
|
...clientProps,
|
|
...(isRSC ? serverProps : {}),
|
|
})
|
|
|
|
return <Component key={key} {...sanitizedProps} />
|
|
}
|
|
|
|
if (typeof Component === 'string' || isPlainObject(Component)) {
|
|
const ResolvedComponent = getFromImportMap<React.ComponentType>({
|
|
importMap,
|
|
PayloadComponent: Component,
|
|
schemaPath: '',
|
|
})
|
|
|
|
if (ResolvedComponent) {
|
|
const isRSC = isReactServerComponentOrFunction(ResolvedComponent)
|
|
|
|
// prevent $undefined from being passed through rsc requests
|
|
const sanitizedProps = removeUndefined({
|
|
...clientProps,
|
|
...(isRSC ? serverProps : {}),
|
|
...(isRSC && typeof Component === 'object' && Component?.serverProps
|
|
? Component.serverProps
|
|
: {}),
|
|
...(typeof Component === 'object' && Component?.clientProps ? Component.clientProps : {}),
|
|
})
|
|
|
|
return <ResolvedComponent key={key} {...sanitizedProps} />
|
|
}
|
|
}
|
|
|
|
return Fallback
|
|
? RenderServerComponent({
|
|
clientProps,
|
|
Component: Fallback,
|
|
importMap,
|
|
key,
|
|
serverProps,
|
|
})
|
|
: null
|
|
}
|