Skip to content

Commit

Permalink
fix(richtext-lexical): minimize the amount of times sanitizeFields is…
Browse files Browse the repository at this point in the history
… called (#6018)
  • Loading branch information
AlessioGr committed Apr 24, 2024
1 parent 8bca0b0 commit 60372fa
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 62 deletions.
Expand Up @@ -40,23 +40,27 @@ export const BlockComponent: React.FC<Props> = (props) => {

const { editorConfig, field: parentLexicalRichTextField } = useEditorConfigContext()

const block = (
editorConfig?.resolvedFeatureMap?.get('blocks')?.props as BlocksFeatureProps
)?.blocks?.find((block) => block.slug === formData?.blockType)
const block = useMemo(
() =>
(editorConfig?.resolvedFeatureMap?.get('blocks')?.props as BlocksFeatureProps)?.blocks?.find(
(block) => block.slug === formData?.blockType,
),
[editorConfig, formData],
)

const unsanitizedFormSchema = block?.fields || []
const formSchema = useMemo(() => {
const unSanitizedFormSchema = block?.fields || []
// Sanitize block's fields here. This is done here and not in the feature, because the payload config is available here

// Sanitize block's fields here. This is done here and not in the feature, because the payload config is available here
const validRelationships = payloadConfig.collections.map((c) => c.slug) || []
const formSchema = transformInputFormSchema(
sanitizeFields({
const validRelationships = payloadConfig.collections.map((c) => c.slug) || []
const sanitizedSchema = sanitizeFields({
config: payloadConfig,
fields: unsanitizedFormSchema,
fields: unSanitizedFormSchema,
requireFieldLevelRichTextEditor: true,
validRelationships,
}),
blockFieldWrapperName,
)
})
return transformInputFormSchema(sanitizedSchema, blockFieldWrapperName)
}, [block, blockFieldWrapperName, payloadConfig])

const initialStateRef = React.useRef<Data>(null) // Store initial value in a ref, so it doesn't change on re-render and only gets initialized once

Expand Down
11 changes: 6 additions & 5 deletions packages/richtext-lexical/src/field/features/Blocks/index.ts
Expand Up @@ -25,11 +25,12 @@ export const BlocksFeature = (props?: BlocksFeatureProps): FeatureProvider => {
if (props?.blocks?.length) {
props.blocks = props.blocks.map((block) => {
const blockCopy = cloneDeep(block)
return {
...blockCopy,
fields: blockCopy.fields.concat(baseBlockFields as FieldWithRichTextRequiredEditor[]),
labels: !blockCopy.labels ? formatLabels(blockCopy.slug) : blockCopy.labels,
}

blockCopy.fields = blockCopy.fields.concat(
baseBlockFields as FieldWithRichTextRequiredEditor[],
)
blockCopy.labels = !blockCopy.labels ? formatLabels(blockCopy.slug) : blockCopy.labels
return blockCopy
})
// unsanitizedBlock.fields are sanitized in the React component and not here.
// That's because we do not have access to the payload config here.
Expand Down
Expand Up @@ -33,13 +33,19 @@ export const blockPopulationPromiseHOC = (
// Sanitize block's fields here. This is done here and not in the feature, because the payload config is available here
const payloadConfig = req.payload.config
const validRelationships = payloadConfig.collections.map((c) => c.slug) || []

blocks.forEach((block) => {
block.fields = sanitizeFields({
config: payloadConfig,
fields: block.fields,
requireFieldLevelRichTextEditor: true,
validRelationships,
})
// @ts-expect-error
if (!block._sanitized) {
block.fields = sanitizeFields({
config: payloadConfig,
fields: block.fields,
requireFieldLevelRichTextEditor: true,
validRelationships,
})
// @ts-expect-error
block._sanitized = true
}
})

// find block used in this node
Expand Down
17 changes: 11 additions & 6 deletions packages/richtext-lexical/src/field/features/Blocks/validate.ts
Expand Up @@ -24,12 +24,17 @@ export const blockValidationHOC = (
// Sanitize block's fields here. This is done here and not in the feature, because the payload config is available here
const validRelationships = payloadConfig.collections.map((c) => c.slug) || []
blocks.forEach((block) => {
block.fields = sanitizeFields({
config: payloadConfig,
fields: block.fields,
requireFieldLevelRichTextEditor: true,
validRelationships,
})
// @ts-expect-error
if (!block._sanitized) {
block.fields = sanitizeFields({
config: payloadConfig,
fields: block.fields,
requireFieldLevelRichTextEditor: true,
validRelationships,
})
// @ts-expect-error
block._sanitized = true
}
})

// find block
Expand Down
Expand Up @@ -23,7 +23,7 @@ import {
} from 'payload/components/utilities'
import { sanitizeFields } from 'payload/config'
import { getTranslation } from 'payload/utilities'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'

import type { LinkFeatureProps } from '../../..'
Expand Down Expand Up @@ -64,8 +64,8 @@ export function LinkEditor({

const [initialState, setInitialState] = useState<Fields>({})

const [fieldSchema] = useState(() => {
const fieldsUnsanitized = transformExtraFields(
const fieldSchema = useMemo(() => {
const fieldsUnSanitized = transformExtraFields(
customFieldSchema,
config,
i18n,
Expand All @@ -74,15 +74,13 @@ export function LinkEditor({
)
// Sanitize custom fields here
const validRelationships = config.collections.map((c) => c.slug) || []
const fields = sanitizeFields({
return sanitizeFields({
config,
fields: fieldsUnsanitized,
fields: fieldsUnSanitized,
requireFieldLevelRichTextEditor: true,
validRelationships,
})

return fields
})
}, [config, customFieldSchema, disabledCollections, enabledCollections, i18n])

const { closeModal, toggleModal } = useModal()
const editDepth = useEditDepth()
Expand Down
@@ -1,4 +1,4 @@
import type { SanitizedCollectionConfig } from 'payload/types'
import type { Field, SanitizedCollectionConfig } from 'payload/types'

import { useModal } from '@faceless-ui/modal'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
Expand All @@ -14,14 +14,16 @@ import {
} from 'payload/components/utilities'
import { sanitizeFields } from 'payload/config'
import { deepCopyObject, getTranslation } from 'payload/utilities'
import React, { useCallback, useEffect, useState } from 'react'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'

import type { ElementProps } from '..'
import type { UploadFeatureProps } from '../..'
import type { BlocksFeatureProps } from '../../../Blocks'
import type { UploadData, UploadNode } from '../../nodes/UploadNode'

import { useEditorConfigContext } from '../../../../lexical/config/EditorConfigProvider'
import { transformInputFormSchema } from '../../../Blocks/utils/transformInputFormSchema'

/**
* This handles the extra fields, e.g. captions or alt text, which are
Expand Down Expand Up @@ -49,19 +51,23 @@ export const ExtraFieldsUploadDrawer: React.FC<
const { closeModal } = useModal()
const { getDocPreferences } = useDocumentInfo()
const [initialState, setInitialState] = useState({})
const fieldSchemaUnsanitized = (
editorConfig?.resolvedFeatureMap.get('upload')?.props as UploadFeatureProps
)?.collections?.[relatedCollection.slug]?.fields

const config = useConfig()

// Sanitize custom fields here
const validRelationships = config.collections.map((c) => c.slug) || []
const fieldSchema = sanitizeFields({
config,
fields: fieldSchemaUnsanitized,
requireFieldLevelRichTextEditor: true,
validRelationships,
})
const fieldSchema = useMemo(() => {
const fieldSchemaUnSanitized = (
editorConfig?.resolvedFeatureMap.get('upload')?.props as UploadFeatureProps
)?.collections?.[relatedCollection.slug].fields

const validRelationships = config.collections.map((c) => c.slug) || []
// Sanitize custom fields here
return sanitizeFields({
config,
fields: fieldSchemaUnSanitized,
requireFieldLevelRichTextEditor: true,
validRelationships,
})
}, [config, editorConfig?.resolvedFeatureMap, relatedCollection.slug])

const handleUpdateEditData = useCallback(
(_, data) => {
Expand All @@ -83,20 +89,11 @@ export const ExtraFieldsUploadDrawer: React.FC<
)

useEffect(() => {
// Sanitize custom fields here
const validRelationships = config.collections.map((c) => c.slug) || []
const fieldSchema = sanitizeFields({
config,
fields: fieldSchemaUnsanitized,
requireFieldLevelRichTextEditor: true,
validRelationships,
})

const awaitInitialState = async () => {
const preferences = await getDocPreferences()
const state = await buildStateFromSchema({
config,
data: deepCopyObject(fields || {}),
data: fields ? deepCopyObject(fields) : {},
fieldSchema,
locale,
operation: 'update',
Expand All @@ -107,8 +104,10 @@ export const ExtraFieldsUploadDrawer: React.FC<
setInitialState(state)
}

void awaitInitialState()
}, [user, locale, t, getDocPreferences, fields, fieldSchemaUnsanitized, config])
if (fieldSchema) {
void awaitInitialState()
}
}, [user, locale, t, getDocPreferences, fields, fieldSchema, config])

return (
<Drawer
Expand Down

0 comments on commit 60372fa

Please sign in to comment.