Files
payload/packages/ui/src/elements/RenderServerComponent/index.tsx
Alessio Gravili 5d2b0b30b0 perf: significantly reduce HTML we send to the client. Up to 4x smaller (#9321)
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:

![CleanShot 2024-11-18 at 20 41
20@2x](https://github.com/user-attachments/assets/edb67d72-f4a5-459b-93f4-68dc65aeffb6)


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 |
2024-11-19 04:30:21 +00:00

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
}