Añadir una galería de imágenes a RichTextEditor en React

El cuadro de diálogo Insert Gallery de RichTextEditor funciona igual en una aplicación React que en una página simple. Lo que cambia es la conexión: el editor es un script del navegador que React no gestiona, el componente tiene que limpiarlo y, durante el desarrollo, las solicitudes del cuadro de diálogo tienen que llegar a tu servidor de API a través del servidor de desarrollo. Esta página muestra una configuración de Vite + React que resuelve las tres cosas, probada con el ejemplo de Express.
Carga los scripts del editor antes que la aplicación
El editor se distribuye como scripts simples que definen window.RichTextEditor. En una aplicación Vite, el lugar correcto más sencillo para ellos es index.html, antes del script de módulo que inicia React:
<!-- Plain browser scripts from public/, loaded before the React bundle. -->
<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css">
<script src="/richtexteditor/rte.js"></script>
<script src="/richtexteditor/plugins/all_plugins.js"></script>
<script src="/rte-files.js"></script>Los archivos están en public/richtexteditor/, que Vite sirve tal cual. rte-files.js conecta los hooks de carga del editor con /rte/upload. Sin él, la galería puede explorar, pero su botón Upload no tiene nada a lo que llamar. Los scripts clásicos de <head> se ejecutan antes que el bundle de módulos, así que window.RichTextEditor ya existe cuando se monta cualquier componente.
El componente
// window.RichTextEditor comes from the scripts in index.html.
export default function RichTextEditor({ options, initialHtml = "", editorRef }) {
const container = useRef(null);
useEffect(() => {
const editor = new window.RichTextEditor(container.current, options);
editor.setHTMLCode(initialHtml);
if (editorRef) editorRef.current = editor;
// Runs on unmount, and once in development: Strict Mode mounts, unmounts, mounts again.
return () => {
if (editorRef) editorRef.current = null;
editor.destroy();
};
}, []); // create once; options are read at creation
return <div ref={container} />;
}El editor se crea una sola vez, en useEffect, sobre el div al que apunta la ref. La función de limpieza llama a destroy(). Eso importa en desarrollo, porque el Strict Mode de React monta cada componente, lo desmonta y lo vuelve a montar. Con la limpieza, el primer editor se destruye antes de crear el segundo. Nuestra comprobación en el navegador, con <StrictMode> activado, encontró exactamente un editor en la página, y el aviso de versión de prueba del editor apareció dos veces en la consola: uno por cada creación. Omitir la limpieza es un error fácil de pasar por alto. Cuando creamos un segundo editor sobre el mismo div sin destroy(), la página seguía mostrando un editor, pero la primera instancia nunca se limpió.
La lista de dependencias vacía es intencionada. Las opciones se leen al crear el editor, así que volver a crearlo en cada render haría perder lo que el usuario escribió. Para leer el contenido, guarda la instancia en una ref y llama a getHTMLCode():
const options = {
toolbar: "full",
galleryEndpoint: "/rte/gallery", // forwarded to Express by vite.config.js
maxUploadFileSize: 4000000,
};En el ejemplo, un botón Show HTML llama a editor.current.getHTMLCode() y mostró <p>Hello from React.</p>. Después de insertar una imagen desde la galería, el HTML contiene <img src="/uploads/...">.
Acceder al servidor de API durante el desarrollo
galleryEndpoint: "/rte/gallery" es relativo a la página, y la página viene de Vite. El proxy de desarrollo de Vite reenvía /rte/ (las rutas de la galería y la carga) y /uploads/ (las imágenes) al ejemplo de Express en el puerto 5101:
// /rte/ and /uploads/ go to the Express sample (node-express, port 5101).
// Keep the trailing slashes: "/rte" alone would also catch /rte-files.js.
proxy: {
"/rte/": "http://localhost:5101",
"/uploads/": "http://localhost:5101",
},El navegador ve un solo origen, así que no hay encabezados CORS que configurar ni URL absolutas en las opciones del editor. Mantén las barras finales. Vite trata una clave simple como prefijo de la URL, así que una clave "/rte" también coincide con /rte-files.js. Lo probamos: con "/rte", /rte-files.js volvió con X-Powered-By: Express, servido por el servidor de API en lugar de por la aplicación. Con "/rte/", Vite sirvió su propia copia.
El proxy es una función de desarrollo. En producción, sirve la aplicación compilada y la API desde un mismo origen, por ejemplo con Express sirviendo dist/ junto a las rutas, o detrás de un único proxy inverso.
Qué muestra el cuadro de diálogo
La imagen del principio es el cuadro de diálogo en esta aplicación React, con las carpetas y las imágenes del servidor Express. La parte del servidor es la misma que en la guía de la galería para Node.js. Hay dos limitaciones que vienen de la galería publicada, no de React. La indicación de formatos enumera JFIF, BMP y AVIF, que el servidor de ejemplo rechaza. Y un archivo rechazado muestra "Upload failed for <name>." en lugar del mensaje del servidor.
Pruébalo
Descarga el proyecto de ejemplo de esta guía y el ejemplo de Express. Copia la carpeta richtexteditor de la descarga del editor en el ejemplo de Express, o define RTE_DIR, e inícialo con npm install y npm start. Después, en el proyecto de React:
npm install
npm run dev
VITE v8.3.5 ready in 440 ms
➜ Local: http://localhost:5111/
curl "http://localhost:5111/rte/gallery?folder="
{"currentFolder":"","currentFolderDisplay":"/","parentFolder":null,"folders":[...],"images":[...]}npm run dev copia primero los archivos del editor en public/. Define RTE_DIR con la carpeta de tu editor, o coloca tú mismo la carpeta en public/richtexteditor. Abre http://localhost:5111, haz clic en Insert Gallery, elige una imagen y haz clic en Insert.