fix memoization and rerendering

This commit is contained in:
Alessio Gravili
2025-09-01 20:22:42 -07:00
parent 70f22da627
commit f1372d1687
3 changed files with 44 additions and 74 deletions

View File

@@ -4,16 +4,15 @@ import type { DefaultTypedEditorState } from '@payloadcms/richtext-lexical'
import type { JSONFieldClientComponent } from 'payload'
import { buildEditorState, useRenderEditor_internal_ } from '@payloadcms/richtext-lexical/client'
import { use, useCallback, useEffect, useRef, useState } from 'react'
import React, { useEffect, useRef, useState } from 'react'
export const OnDemand: JSONFieldClientComponent = (args) => {
const { Component, renderLexical } = useRenderEditor_internal_({
name: 'richText',
editorTarget: 'default',
})
// mount the lexical runtime once
const mounted = useRef(false)
useEffect(() => {
if (mounted.current) {
return
@@ -22,49 +21,20 @@ export const OnDemand: JSONFieldClientComponent = (args) => {
mounted.current = true
}, [renderLexical])
// build the initial editor state once, with lazy init (no ref reads in render)
const [initialValue] = useState<DefaultTypedEditorState | undefined>(() =>
const [value, setValue] = useState<DefaultTypedEditorState | undefined>(() =>
buildEditorState({ text: 'state default' }),
)
// keep latest content in a ref so updates dont trigger React renders
const latestValueRef = useRef<DefaultTypedEditorState | undefined>(initialValue)
// stable setter given to the editor; updates ref only
const setValueStable = useCallback((next: DefaultTypedEditorState | undefined) => {
// absolutely no state set here; no React re-render, no remount
latestValueRef.current = next
// if you later get access to the editor instance, this is where you'd imperatively sync it
const handleReset = React.useCallback(() => {
setValue(buildEditorState({ text: 'state default' }))
}, [])
// If you need a "reset to default," and the editor doesn't expose an imperative API,
// the only reliable way is a key bump to force a remount ON RESET ONLY.
// This does not affect normal setValue cycles.
const [resetNonce, setResetNonce] = useState(0)
const handleReset = useCallback(() => {
latestValueRef.current = initialValue
// If you have an imperative API: editor.setEditorState(initialValue)
// Otherwise, remount once to guarantee visual reset:
setResetNonce((n) => n + 1)
}, [initialValue])
return (
<div>
<div>Default Component:</div>
{Component ? (
<Component
key={resetNonce}
// editor will call this; we won't re-render on its calls
setValue={setValueStable as any}
// initial value only; never changes so the element wont re-render because of this prop
value={initialValue}
/>
) : (
'Loading...'
)}
Default Component:
{Component ? <Component setValue={setValue as any} value={value} /> : 'Loading...'}
<button onClick={handleReset} style={{ marginTop: 8 }} type="button">
Reset to Default
Reset Editor State
</button>
</div>
)