Gestionar el estado del editor en React
Usa un único propietario para el documento actual. Elige el estado controlado cuando el resto de tu aplicación necesite cada cambio; elige un editor no controlado cuando sobre todo necesites leer el contenido al guardar. Estos ejemplos usan el binding de React de npm en modo HTML.
Controlado: mantén actualizado el estado local
"use client";
import { useState } from "react";
import { RichTextEditor } from "@richscripts/richtexteditor/react";
export default function Composer() {
const [html, setHtml] = useState("<p>Start writing...</p>");
return <RichTextEditor
value={html}
onChange={(next) => {
if (typeof next === "string") setHtml(next);
}}
/>;
}Conserva el valor que emite el editor como tu valor de edición local. Aplica debounce a la persistencia en red por separado, en lugar de devolverle a propósito al editor un valor más antiguo. Evita sustituir el documento entero por una copia normalizada por el servidor después de cada guardado.
El binding distingue los valores que emite él mismo de las sustituciones externas. Eso no hace que cualquier transformación de la aplicación respete la selección. Un controlador onChange que reformatea el HTML y devuelve un contenido distinto le está pidiendo al editor que sustituya el contenido mientras alguien escribe.
No controlado: lee el HTML actual al guardar
"use client";
import { useRef, useState } from "react";
import { RichTextEditor } from "@richscripts/richtexteditor/react";
import type { RichTextEditorReactHandle } from "@richscripts/richtexteditor/react";
export default function Draft({ initialHtml, onSave }: {
initialHtml: string;
onSave: (html: string) => void;
}) {
const editorRef = useRef<RichTextEditorReactHandle>(null);
const [ready, setReady] = useState(false);
return <>
<RichTextEditor ref={editorRef} defaultValue={initialHtml}
onReady={() => setReady(true)} />
<button type="button" disabled={!ready} onClick={() => {
if (editorRef.current?.getEditor()) {
onSave(editorRef.current.getHTMLCode());
}
}}>Save</button>
</>;
}defaultValue inicializa el documento; no es una forma de enviar cambios posteriores a un editor ya montado. La ref expone getHTMLCode(). La comprobación de que el editor está listo evita que un guardado prematuro trate como documento vacío un editor que todavía no se ha inicializado.
Este ejemplo entrega el HTML al callback de tu aplicación; no implementa la persistencia, el fin de las cargas ni la recuperación de errores al guardar. Si las imágenes están activadas, usa la secuencia de carga y guardado antes de leer el HTML definitivo. Valida y sanea en el servidor el contenido enviado.
Cambia de documento y gestiona las respuestas del servidor de forma segura
- Usa una identidad de documento estable. Una
keybasada en el HTML actual o en un número aleatorio vuelve a montar el editor durante la edición. - Una key basada a propósito en el id del documento puede iniciar un editor nuevo para otro documento. Confirma o guarda un borrador con cambios antes de cambiar; volver a montar descarta el estado de la instancia, como la selección y el historial de deshacer.
- Si cargas los documentos de forma asíncrona, ignora o cancela la respuesta de un documento que ya no está seleccionado.
- Lleva un control de la revisión del documento al guardar. Una respuesta tardía debe actualizar el estado de guardado de su propia revisión, no sobrescribir un borrador más reciente. Hacen falta comprobaciones de revisión en el servidor para detectar el guardado conflictivo de otro usuario.
- No cambies entre los modos controlado y no controlado a mitad de sesión. Elige el patrón al abrir el documento.
Montaje y Strict Mode
Usa el wrapper de React incluido en lugar de crear un segundo editor sobre el mismo elemento host. Si escribes tu propio wrapper, crea el editor cuando ya exista el host, desconecta los listeners propios y destruye la instancia propia en la limpieza. Ignora la inicialización asíncrona que termine después del desmontaje.
El Strict Mode de React puede ejecutar un ciclo adicional de configuración y limpieza en desarrollo. Mantenlo activado mientras compruebas el comportamiento del ciclo de vida. Para Next.js, sigue la guía de integración con componentes de cliente; mostrar el contenido guardado y montar un editor interactivo son cuestiones distintas.
Lista de comprobación para solucionar problemas
- El cursor salta: revisa las keys que cambian, los componentes que se vuelven a crear, las llamadas repetidas a
setHTMLCode()y los valores controlados transformados. - Lo que se escribe desaparece: revisa los valores obsoletos que llegan de callbacks retrasados, respuestas de guardado y cargas de documentos.
- El guardado pierde la última edición: lee la ref del editor en el momento del envío en lugar de suponer que una notificación de cambio retrasada ya ha actualizado el estado de React.
- Barras de herramientas duplicadas: revisa la inicialización y la limpieza duplicadas, incluidos los cambios de ruta y la reapertura de ventanas modales.
- Los borradores grandes van lentos: mide por separado la serialización del HTML, el renderizado de la vista previa y los guardados en red.
- La entrada con IME falla: prueba la composición en chino o japonés sin reescribir el contenido durante la composición, y comprueba el deshacer después de confirmar el texto.
A continuación: diagnostica paso a paso los saltos del cursor, instala el binding de React o prueba el envío de formularios y guardar y cargar.
Referencias: React Strict Mode y conservar y restablecer el estado.