Eine Bildergalerie für RichTextEditor in React

Der Dialog „Insert Gallery“ im Beispiel mit Vite + React, mit den Ordnern blog, products und team und vier Bildern vom Express-Server

Der Dialog „Insert Gallery“ von RichTextEditor funktioniert in einer React-App genauso wie auf einer einfachen Seite. Anders ist die Verdrahtung: Der Editor ist ein Browserskript, das React nicht verwaltet, die Komponente muss ihn wieder aufräumen, und während der Entwicklung müssen die Requests des Dialogs Ihren API-Server über den Dev-Server erreichen. Diese Seite zeigt ein Setup mit Vite + React, das alle drei Punkte erfüllt, getestet mit dem Express-Beispiel.

Die Editor-Skripte vor der App laden

Der Editor wird als einfache Skripte ausgeliefert, die window.RichTextEditor definieren. In einer Vite-App ist index.html der einfachste korrekte Ort dafür, vor dem Modulskript, das React startet:

HTML · index.html
<!-- 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>

Die Dateien liegen in public/richtexteditor/, das Vite unverändert ausliefert. rte-files.js verbindet die Upload-Hooks des Editors mit /rte/upload. Ohne diese Datei kann die Galerie durchsuchen, aber ihre Upload-Schaltfläche hat nichts, was sie aufrufen kann. Klassische Skripte in <head> laufen vor dem Modul-Bundle, daher existiert window.RichTextEditor, sobald irgendeine Komponente gemountet wird.

Die Komponente

JavaScript · src/RichTextEditor.jsx
// 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} />;
}

Der Editor wird einmal erstellt, in useEffect, auf dem div, auf das die Ref zeigt. Die Cleanup-Funktion ruft destroy() auf. Das ist in der Entwicklung wichtig, weil der Strict Mode von React jede Komponente mountet, unmountet und erneut mountet. Mit der Cleanup-Funktion wird der erste Editor zerstört, bevor der zweite erstellt wird. Unsere Prüfung im Browser mit aktiviertem <StrictMode> fand genau einen Editor auf der Seite, und der Hinweis der Testversion erschien zweimal in der Konsole: einmal pro Erstellung. Dass die Cleanup-Funktion fehlt, bemerkt man leicht nicht. Als wir ohne destroy() einen zweiten Editor auf demselben div erstellt haben, zeigte die Seite zwar weiterhin einen Editor, die erste Instanz wurde aber nie aufgeräumt.

Die leere Abhängigkeitsliste ist Absicht. Die Optionen werden beim Erstellen des Editors gelesen, und würde der Editor bei jedem Rendern neu erstellt, ginge verloren, was der Benutzer getippt hat. Um den Inhalt zu lesen, halten Sie die Instanz in einer Ref und rufen getHTMLCode() auf:

JavaScript · src/App.jsx
const options = {
  toolbar: "full",
  galleryEndpoint: "/rte/gallery", // forwarded to Express by vite.config.js
  maxUploadFileSize: 4000000,
};

Im Beispiel ruft eine Schaltfläche „Show HTML“ editor.current.getHTMLCode() auf und gab <p>Hello from React.</p> aus. Nachdem ein Bild aus der Galerie eingefügt wurde, enthält das HTML <img src="/uploads/...">.

Den API-Server in der Entwicklung erreichen

galleryEndpoint: "/rte/gallery" ist relativ zur Seite, und die Seite kommt von Vite. Der Dev-Proxy von Vite leitet /rte/ (die Routen für Galerie und Upload) und /uploads/ (die Bilder) an das Express-Beispiel auf Port 5101 weiter:

JavaScript · vite.config.js
// /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",
},

Der Browser sieht nur einen Origin, also müssen Sie keine CORS-Header setzen und keine absoluten URLs in den Editor-Optionen angeben. Behalten Sie die abschließenden Schrägstriche bei. Vite vergleicht einen einfachen Schlüssel als Präfix der URL, daher passt ein Schlüssel "/rte" auch auf /rte-files.js. Wir haben es ausprobiert: Mit "/rte" kam /rte-files.js mit X-Powered-By: Express zurück, ausgeliefert vom API-Server statt von der App. Mit "/rte/" lieferte Vite seine eigene Kopie aus.

Der Proxy ist eine Funktion für die Entwicklung. In der Produktion liefern Sie die gebaute App und die API von einem Origin aus, zum Beispiel mit Express, das dist/ neben den Routen ausliefert, oder hinter einem gemeinsamen Reverse Proxy.

Was der Dialog anzeigt

Das Bild oben zeigt den Dialog in dieser React-App, mit den Ordnern und Bildern vom Express-Server. Die Serverseite ist dieselbe wie in der Galerie-Anleitung für Node.js. Zwei Einschränkungen stammen von der ausgelieferten Galerie, nicht von React. Der Formathinweis nennt JFIF, BMP und AVIF, die der Beispielserver ablehnt. Und eine abgelehnte Datei zeigt „Upload failed for <name>.“ statt der Meldung des Servers.

Testen

Laden Sie das Beispielprojekt zu dieser Anleitung und das Express-Beispiel herunter. Kopieren Sie den Ordner richtexteditor aus dem Editor-Download in das Express-Beispiel oder setzen Sie RTE_DIR, und starten Sie es mit npm install und npm start. Dann im React-Projekt:

Shell
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 kopiert zuerst die Editor-Dateien nach public/. Setzen Sie RTE_DIR auf Ihren Editor-Ordner oder legen Sie den Ordner selbst unter public/richtexteditor ab. Öffnen Sie http://localhost:5111, klicken Sie auf Insert Gallery, wählen Sie ein Bild und klicken Sie auf Insert.

Dieselbe Anleitung für andere Plattformen

Vue · Angular

Weiterlesen