{"id":10330,"date":"2018-08-17T09:10:14","date_gmt":"2018-08-17T14:10:14","guid":{"rendered":"http:\/\/carterembry.com\/\/?p=10330"},"modified":"2025-08-05T09:05:23","modified_gmt":"2025-08-05T14:05:23","slug":"creating-flexible-and-reusable-react-file-uploaders","status":"publish","type":"post","link":"https:\/\/carterembry.com\/de\/2018\/creating-flexible-and-reusable-react-file-uploaders\/%20","title":{"rendered":"Flexible und wiederverwendbare React File Uploader erstellen"},"content":{"rendered":"<div id=\"pl-10330\"  class=\"panel-layout\" ><div id=\"pg-10330-0\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-0-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-0-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"0\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Daniel wurde k\u00fcrzlich im Eventbrite Engineering Blog vorgestellt und wir sind sehr stolz auf die Arbeit, die er dort leistet.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-1\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-1-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-1-0-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"1\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/Flexible-Reusable-React-File-Uploader.png\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/Flexible-Reusable-React-File-Uploader.png\" width=\"538\" height=\"300\" sizes=\"(max-width: 538px) 100vw, 538px\" title=\"\t\t\t\t\t\t\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><div id=\"pgc-10330-1-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-1-1-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"2\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Das Eventbrite-Team brauchte einen React-basierten Bild-Uploader, der flexibel ist und gleichzeitig eine einfache Benutzeroberfl\u00e4che bietet. Die Komponenten des Bild-Uploaders sollten in einer Vielzahl von Szenarien als wiederverwendbare Teile funktionieren, die je nach Bedarf unterschiedlich zusammengestellt werden k\u00f6nnen. Lies weiter, um zu sehen, wie wir dieses Problem gel\u00f6st haben.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-2\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-2\" ><div id=\"pgc-10330-2-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-2-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"3\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">Was ist ein Datei-Uploader?<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-3\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-3\" ><div id=\"pgc-10330-3-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-3-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"4\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Wenn du in der Vergangenheit eine Datei von deinen Webnutzern abrufen wolltest, musstest du eine\u00a0<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/input\/file\">Eingabetyp \"Datei\"<\/a>. Dieser Ansatz war in vielerlei Hinsicht begrenzt, vor allem dadurch, dass\u00a0<i>es ist eine Eingabe<\/i>: Die Daten werden erst \u00fcbertragen, wenn du das Formular abschickst, sodass die Nutzer keine M\u00f6glichkeit haben, vor oder w\u00e4hrend des Hochladens ein Feedback zu sehen.<\/p>\n<p>In diesem Sinne sind die React-Uploader, \u00fcber die wir sprechen werden, keine Formulareingaben, sondern \"Sofort-Transport-Tools\". Der Nutzer w\u00e4hlt eine Datei aus, der Uploader transportiert sie zu einem entfernten Server und erh\u00e4lt dann eine Antwort mit einer eindeutigen Kennung. Diese Kennung wird dann sofort mit einem Datenbankeintrag verkn\u00fcpft oder in ein verstecktes Formularfeld eingetragen.<\/p>\n<\/div>\n<\/div><\/div><\/div><div id=\"pgc-10330-3-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-3-1-0\" class=\"so-panel widget widget_sow-video panel-first-child panel-last-child\" data-index=\"5\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-video so-widget-sow-video-default-f379b959fc93-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-video-wrapper\">\n\t\t\t<video\n\t\t\tid=\"sow-player-1\" class=\"sow-video-widget\" preload=\"auto\" style=\"width:100%;height:100%;\" controls>\n\t\t\t\t\t\t\t<source type=\"video\/mp4\" src=\"https:\/\/carterembry.com\/wp-content\/uploads\/2022\/02\/uploaders.mp4\"\/>\n\t\t\t\t\t<\/video>\n\t<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-4\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-4-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-4-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"6\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Diese neue Strategie bietet eine enorme Flexibilit\u00e4t gegen\u00fcber herk\u00f6mmlichen Upload-Prozessen. Durch die Entkopplung des Dateitransports von den Formular\u00fcbermittlungen k\u00f6nnen wir zum Beispiel\u00a0<strong>direkt auf Speicherpl\u00e4tze von Drittanbietern (wie Amazon S3) hochladen, ohne die Dateien \u00fcber unsere Server zu schicken<\/strong>.<\/p>\n<p>Der Kompromiss f\u00fcr diese Flexibilit\u00e4t ist die Komplexit\u00e4t: Drag-and-Drop-Datei-Uploader sind komplexe Gebilde. Au\u00dferdem sollte unser React-Uploader einfach und benutzerfreundlich sein. Es war keine leichte Aufgabe, einen Weg zu finden, der sowohl Flexibilit\u00e4t als auch Benutzerfreundlichkeit bietet.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-5\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-5-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-5-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"7\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">Verantwortlichkeiten festlegen<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Die Aufgaben eines Uploaders zu bestimmen, scheint einfach zu sein... er l\u00e4dt hoch, richtig? Sicherlich, aber es gibt noch eine Menge anderer Dinge, die dazu geh\u00f6ren, damit das passiert:<\/p>\n<ul>\n<li>Sie muss eine Drop-Zone haben, die sich je nach Benutzerinteraktion \u00e4ndert. Wenn der\/die Nutzer\/in eine Datei dar\u00fcber zieht, sollte sie diese Zustands\u00e4nderung anzeigen.<\/li>\n<li>Was ist, wenn unsere Nutzer keine Dateien ziehen k\u00f6nnen? Vielleicht haben sie Probleme mit der Barrierefreiheit oder sie versuchen, von ihrem Handy aus hochzuladen. In jedem Fall muss unser Uploader eine Dateiauswahl anzeigen, wenn der Nutzer klickt oder tippt.<\/li>\n<li>Es muss die ausgew\u00e4hlte Datei \u00fcberpr\u00fcfen, um sicherzustellen, dass sie einen akzeptablen Typ und eine akzeptable Gr\u00f6\u00dfe hat.<\/li>\n<li>Sobald eine Datei ausgew\u00e4hlt wurde, sollte eine Vorschau der Datei angezeigt werden, w\u00e4hrend sie hochgeladen wird.<\/li>\n<li>Sie sollte dem\/der Nutzer\/in w\u00e4hrend des Hochladens ein sinnvolles Feedback geben, z.B. einen Fortschrittsbalken oder eine Ladegrafik, die anzeigt, dass etwas passiert.<\/li>\n<li>Und was ist, wenn es fehlschl\u00e4gt? Es muss dem Benutzer eine aussagekr\u00e4ftige Fehlermeldung angezeigt werden, damit er wei\u00df, dass er es noch einmal versuchen (oder aufgeben) soll.<\/li>\n<li>Oh, und die Datei muss tats\u00e4chlich hochgeladen werden.<\/li>\n<\/ul>\n<p>Diese Aufgaben sind nur eine kurze Liste, aber du verstehst schon, dass sie sehr schnell kompliziert werden k\u00f6nnen. Au\u00dferdem m\u00fcssen beim Hochladen\u00a0<i>Bilder<\/i>\u00a0unser Hauptanwendungsfall ist, kann es eine Vielzahl von Anforderungen f\u00fcr das Hochladen von Dateien geben. Wenn du dir schon die M\u00fche gemacht hast, das Verhalten von Drag \/ Drop \/ Markieren \/ Validieren \/ Transportieren \/ Erfolg \/ Misserfolg herauszufinden, warum solltest du dann alles noch einmal schreiben, wenn du pl\u00f6tzlich CSV-Dateien f\u00fcr diesen einen Bericht hochladen musst?<\/p>\n<p>Wie k\u00f6nnen wir also unseren React Image Uploader strukturieren, um maximale Flexibilit\u00e4t und Wiederverwendbarkeit zu erreichen?<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-6\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-6\" ><div id=\"pgc-10330-6-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-6-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"8\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">Trennung der Belange<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-7\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-7\" ><div id=\"pgc-10330-7-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-7-0-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"9\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__02.jpeg\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__02.jpeg\" width=\"1152\" height=\"414\" sizes=\"(max-width: 1152px) 100vw, 1152px\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><div id=\"pgc-10330-7-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-7-1-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"10\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Im folgenden Diagramm siehst du einen \u00dcberblick \u00fcber unseren geplanten Ansatz. Keine Sorge, wenn es dir kompliziert vorkommt - wir werden jede dieser Komponenten weiter unten genauer unter die Lupe nehmen, um mehr \u00fcber ihren Zweck und ihre Rolle zu erfahren.<\/p>\n<p>Unsere React-basierte Komponentenbibliothek sollte nicht wissen m\u00fcssen, wie unsere APIs funktionieren. Diese Logik getrennt zu halten, hat den zus\u00e4tzlichen Vorteil der Wiederverwendbarkeit; verschiedene Produkte sind nicht an eine einzige API oder sogar an einen einzigen Stil der API gebunden. Stattdessen k\u00f6nnen sie so viel oder so wenig wiederverwenden, wie sie brauchen.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-8\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-8-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-8-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"11\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Auch innerhalb\u00a0<a href=\"https:\/\/medium.com\/@dan_abramov\/smart-and-dumb-components-7ca2f9a7c7d0\">Pr\u00e4sentationskomponenten<\/a>gibt es die M\u00f6glichkeit, Funktion und Pr\u00e4sentation zu trennen. Wir haben also unsere Liste der Aufgaben genommen und einen Stapel von Komponenten erstellt, der von der allgemeinsten ganz unten bis zur spezifischsten ganz oben reicht.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-9\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-9\" ><div id=\"pgc-10330-9-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-9-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"12\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">Grundlegende Komponenten<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<h4>UploaderDropzone<\/h4>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-10\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-10\" ><div id=\"pgc-10330-10-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-10-0-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"13\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__03.jpeg\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__03.jpeg\" width=\"1152\" height=\"413\" sizes=\"(max-width: 1152px) 100vw, 1152px\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><div id=\"pgc-10330-10-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-10-1-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"14\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Diese Komponente ist das Herzst\u00fcck der Uploader-Benutzeroberfl\u00e4che, wo die Aktion beginnt. Sie verarbeitet Drag\/Drop-Ereignisse und Click-to-Browse. Sie hat selbst keinen Status, sondern wei\u00df nur, wie sie sich normalisiert und auf bestimmte Benutzeraktionen reagiert (siehst du, was ich da gemacht habe?). Er akzeptiert R\u00fcckrufe als Requisiten, damit er seinem Implementierer mitteilen kann, wann etwas passiert.<\/p>\n<p>Es wartet darauf, dass Dateien dar\u00fcber gezogen werden und ruft dann einen Callback auf. Wenn eine Datei ausgew\u00e4hlt wird, entweder durch Ziehen\/Ablegen oder durch Klicken zum Durchsuchen, wird ein weiterer Callback mit dem JS-Dateiobjekt aufgerufen.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-11\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-11-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-11-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"15\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Sie hat eine der Eingaben vom Typ \"Datei\", die ich bereits erw\u00e4hnt habe, f\u00fcr Benutzer, die keine Dateien ziehen k\u00f6nnen (oder wollen). Diese Funktion ist wichtig. Indem wir sie hier abstrahieren, m\u00fcssen die Komponenten, die die Dropzone verwenden, nicht dar\u00fcber nachdenken, wie die Datei ausgew\u00e4hlt wurde.<\/p>\n<p>Im Folgenden siehst du ein Beispiel f\u00fcr UploaderDropzone mit React:<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-12\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-12\" ><div id=\"pgc-10330-12-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-12-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child\" data-index=\"16\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n\nZiehe eine Datei hierher!\n\n<\/pre>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-12-0-1\" class=\"so-panel widget widget_sow-editor panel-last-child\" data-index=\"17\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>UploaderDropzone hat sehr wenig Meinung dar\u00fcber, wie es aussieht, und hat daher nur ein minimales Styling. Zum Beispiel,\u00a0<a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=656164\">einige Browser behandeln Drag-Events unterschiedlich<\/a>\u00a0wenn sie auf tiefen Nachkommen des Zielknotens auftreten. Um dieses Problem zu l\u00f6sen, verwendet die Dropzone ein einziges transparentes Div, das alle Unterknoten abdeckt. Dies sorgt f\u00fcr die n\u00f6tige Erfahrung f\u00fcr Nutzer, die ziehen\/ablegen, aber auch\u00a0<strong>die Zug\u00e4nglichkeit f\u00fcr Bildschirmleser und andere unterst\u00fctzende Technologien aufrechterh\u00e4lt.<\/strong><\/p>\n<\/div>\n<\/div><\/div><\/div><div id=\"pgc-10330-12-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-12-1-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"18\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__04.jpeg\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__04.jpeg\" width=\"725\" height=\"458\" sizes=\"(max-width: 725px) 100vw, 725px\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-13\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-13\" ><div id=\"pgc-10330-13-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-13-0-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"19\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__05.jpeg\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__05.jpeg\" width=\"761\" height=\"471\" sizes=\"(max-width: 761px) 100vw, 761px\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><div id=\"pgc-10330-13-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-13-1-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"20\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<h4>UploaderLayoutManager<\/h4>\n<p>Die Komponente UploaderLayoutManager verwaltet die meisten Zustands\u00fcberg\u00e4nge und wei\u00df, welches Layout f\u00fcr jeden Schritt des Prozesses angezeigt werden soll, w\u00e4hrend sie andere React-Komponenten als Requisiten f\u00fcr jeden Schritt akzeptiert.<\/p>\n<p><strong>Dies erm\u00f6glicht es, jeden Schritt als separate visuelle Idee zu betrachten, ohne sich Gedanken dar\u00fcber zu machen, wie und wann jeder \u00dcbergang stattfindet.<\/strong>. Die Unterst\u00fctzer dieser React-Komponente m\u00fcssen sich nur Gedanken dar\u00fcber machen, welches Layout zu einem bestimmten Zeitpunkt je nach Zustand sichtbar sein soll, nicht aber dar\u00fcber, wie die Dateien aufgef\u00fcllt werden oder wie das Layout aussehen soll.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-14\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-14-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-14-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child\" data-index=\"21\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Hier ist eine Liste von Schritten, die dem LayoutManager als Requisiten \u00fcbergeben werden k\u00f6nnen:<\/p>\n<ul>\n<li>Schritte, die vom LayoutManager verwaltet werden:\n<ul>\n<li>Unbev\u00f6lkert - eine leere Dropzone mit einem Aufruf zur Aktion (\"Lade ein tolles Bild hoch!\")<\/li>\n<li>Datei wird \u00fcber das Fenster gezogen, aber nicht \u00fcber die Dropzone (\"Lass die Datei hier fallen!\")<\/li>\n<li>Datei wird \u00fcber Dropzone gezogen (\"Jetzt fallen lassen!\")<\/li>\n<li>Datei-Upload l\u00e4uft (\"Warte, ich schicke es...\")<\/li>\n<\/ul>\n<\/li>\n<li>Schritt, der von einer Komponente verwaltet wird, die den LayoutManager implementiert:\n<ul>\n<li>Die Datei wurde hochgeladen und wird ausgef\u00fcllt. F\u00fcr unseren Bild-Uploader ist dies eine Vorschau des Bildes mit einer Schaltfl\u00e4che \"Entfernen\".<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-14-0-1\" class=\"so-panel widget widget_sow-editor\" data-index=\"22\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Der LayoutManager selbst hat wenig oder gar keine Stile und zeigt nur Bildmaterial an, das als Requisiten \u00fcbergeben wurde. Er ist daf\u00fcr verantwortlich, dass der Benutzer wei\u00df, welchen Schritt im Prozess er erreicht hat, und zeigt die Inhalte f\u00fcr diesen Schritt an.<\/p>\n<p>Der einzige Layout-Schritt, der extern verwaltet wird, ist \"Vorschau\" (ob der Uploader ein Bild eingef\u00fcgt hat). Das liegt daran, dass die implementierende Komponente den Zustand festlegen muss, in dem der Uploader startet. Wenn der Nutzer z. B. zuvor ein Bild hochgeladen hat, soll dieses Bild angezeigt werden, wenn er auf die Seite zur\u00fcckkehrt.<\/p>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-14-0-2\" class=\"so-panel widget widget_sow-editor\" data-index=\"23\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Beispiel f\u00fcr die Verwendung des LayoutManagers:<\/p>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-14-0-3\" class=\"so-panel widget widget_sow-editor panel-last-child\" data-index=\"24\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n&lt;uploaderlayoutmanager\n    dropzoneElement={}\n    windowDragDropzoneElement={}\n    dragDropzoneElement={}\n    loadingElement={}\n    previewElement={}\n \n    showPreview={!!file}\n \n    onReceiveFile={handleReceiveFile}\n\/&gt;\n<\/pre>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-15\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-15\" ><div id=\"pgc-10330-15-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-15-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"25\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">Ressourcen-spezifische Komponenten<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<h4>ImageUploader<\/h4>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-16\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-16\" ><div id=\"pgc-10330-16-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-16-0-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"26\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__06.jpeg\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__06.jpeg\" width=\"1152\" height=\"414\" sizes=\"(max-width: 1152px) 100vw, 1152px\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><div id=\"pgc-10330-16-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-16-1-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"27\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Die ImageUploader-Komponente ist fast ausschlie\u00dflich auf die Pr\u00e4sentation ausgerichtet. Sie definiert das Aussehen der einzelnen Schritte und \u00fcbergibt sie als Requisiten an einen UploadLayoutManager. Hier ist auch ein guter Platz f\u00fcr die Validierung (Dateityp, Dateigr\u00f6\u00dfe usw.).<\/p>\n<p>Unterst\u00fctzer dieses Tools k\u00f6nnen sich fast ausschlie\u00dflich auf das optische Erscheinungsbild des Uploaders konzentrieren. Diese Komponente enth\u00e4lt nur sehr wenig Logik, da die Zustands\u00fcberg\u00e4nge vom UploaderLayoutManager verwaltet werden.\u00a0<strong>Wir k\u00f6nnen das Bildmaterial flie\u00dfend \u00e4ndern, ohne uns Sorgen zu machen, dass die Funktion des Uploaders beeintr\u00e4chtigt wird.<\/strong><\/p>\n<p>&nbsp;<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-17\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-17-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-17-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child\" data-index=\"28\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Beispiel ImageUploader:<\/p>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-17-0-1\" class=\"so-panel widget widget_sow-editor panel-last-child\" data-index=\"29\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nconst DropzoneLayout = () =&amp;gt; (\n    &lt;p&gt;Ziehe eine Datei hierher oder klicke zum Durchsuchen&lt;\/p&gt;\n);\nconst DragDropzoneLayout = () =&amp;gt; (\n    &lt;p&gt;Datei jetzt ablegen!&lt;\/p&gt;\n);\nconst LoadingLayout = () =&amp;gt; (\n    &lt;p&gt;Bitte warten, laden...&lt;\/p&gt;\n);\nconst PreviewLayout = ({file, onRemove}) =&amp;gt; (\n    &lt;div&gt;\n        &lt;p&gt;Name: {Datei.Name}&lt;\/p&gt;\n        &lt;button onclick=&quot;{onRemove}&quot;&gt;Datei entfernen&lt;\/button&gt;\n    &lt;\/div&gt;\n);\nclass ImageUploader extends React.Component {\n    state = {file: undefined};\n \n    _handleRemove = () =&amp;gt; this.setState({file: undefined});\n \n    _handleReceiveFile = (file) =&amp;gt; {\n        this.setState({file}));\n \n        return new Promise((resolve, reject) =&amp;gt; {\n            \/\/ Lade die Datei hoch!\n        })\n        .catch(() =&amp;gt; this.setState({file: undefined}))\n    }\n \n    render() {\n        let {file} = this.state;\n        let Vorschau;\n \n        if (file) {\n            preview = (\n                &lt;previewlayout file=&quot;{file}&quot; onremove=&quot;{this._handleRemove}&quot;&gt;&lt;\/previewlayout&gt;\n            );\n        }\n \n        return (\n            &lt;uploaderlayoutmanager\n                dropzoneelement=&quot;{&lt;DropzoneLayout&quot;&gt;&lt;\/uploaderlayoutmanager&gt;}\n                dragDropzoneElement={&lt;dragdropzonelayout&gt;&lt;\/dragdropzonelayout&gt;}\n                loadingElement={&lt;loadinglayout&gt;&lt;\/loadinglayout&gt;}\n                previewElement={Vorschau}\n                showPreview={!!file}\n                onReceiveFile={this._handleReceiveFile}\n            \/&amp;gt;\n        );\n    }\n};\n<\/pre>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-18\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-18\" ><div id=\"pgc-10330-18-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-18-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"30\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">Anwendungsspezifische Schicht<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-19\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-19\" ><div id=\"pgc-10330-19-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-19-0-0\" class=\"so-panel widget widget_sow-image panel-first-child panel-last-child\" data-index=\"31\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-image so-widget-sow-image-default-dbf295114b96-10330\"\n\t\t\t\n\t\t>\n<div class=\"sow-image-container\">\n\t\t\t<a href=\"http:\/\/carterembry.com\/\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__07.jpeg\"\n\t\t\t\t\t>\n\t\t\t<img \n\tsrc=\"https:\/\/carterembry.com\/wp-content\/uploads\/2018\/08\/flexible_resuable_react_uploader__07.jpeg\" width=\"1152\" height=\"415\" sizes=\"(max-width: 1152px) 100vw, 1152px\" alt=\"\" \t\tclass=\"so-widget-image\" loading=\"lazy\" \/>\n\t\t\t<\/a><\/div>\n\n<\/div><\/div><\/div><div id=\"pgc-10330-19-1\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-19-1-0\" class=\"so-panel widget widget_sow-editor panel-first-child panel-last-child\" data-index=\"32\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Das obige Beispiel hat einen auff\u00e4lligen Aspekt, der nichts mit der Pr\u00e4sentation zu tun hat: der Dateitransport, der in _handleReceiveFile stattfindet. Wir wollen, dass diese ImageUploader-Komponente in unserer Komponentenbibliothek lebt und vom API-spezifischen Verhalten entkoppelt ist, also m\u00fcssen wir das entfernen. Zum Gl\u00fcck ist es so einfach, wie eine Funktion \u00fcber props zu akzeptieren, die ein Versprechen zur\u00fcckgibt, das aufgel\u00f6st wird, wenn der Upload abgeschlossen ist.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><\/div><div id=\"pg-10330-20\"  class=\"panel-grid panel-no-style\" ><div id=\"pgc-10330-20-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-20-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child\" data-index=\"33\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n_handleReceiveFile(file) {\n    \/\/ Hier k\u00f6nnte die Datei vor dem Transport gepr\u00fcft werden. Wenn die Datei die Validierung nicht besteht, wird ein abgelehntes Versprechen zur\u00fcckgegeben.\n    let {uploadImage} = this.props;\n \n    this.setState({file});\n \n    return uploadImage(file)\n        .catch(() =&gt; this.setState({file: undefined}))\n}\n<\/pre>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-20-0-1\" class=\"so-panel widget widget_sow-editor\" data-index=\"34\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Mit dieser kleinen \u00c4nderung kann derselbe Bild-Uploader f\u00fcr eine Vielzahl von Anwendungen genutzt werden.\u00a0<strong>Ein Teil deiner Anwendung kann Bilder direkt zu einem Drittanbieter (z. B. Amazon S3) hochladen, w\u00e4hrend ein anderer Teil Bilder zu einem v\u00f6llig anderen Zweck und f\u00fcr eine v\u00f6llig andere Handhabung auf einen lokalen Server hochl\u00e4dt, aber die gleiche visuelle Darstellung verwendet.<\/strong><\/p>\n<p>Und weil diese ganze Komplexit\u00e4t in jede Komponente aufgeteilt ist, hat der ImageUploader eine sehr saubere Implementierung:<\/p>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-20-0-2\" class=\"so-panel widget widget_sow-editor\" data-index=\"35\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n&lt;imageuploader uploadImage={S3ImageUploadApi}&gt;&lt;\/imageuploader&gt;\n<\/pre>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-20-0-3\" class=\"so-panel widget widget_sow-editor panel-last-child\" data-index=\"36\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Mit dieser Grundlage k\u00f6nnen Anwendungen denselben ImageUploader auf verschiedene Weise nutzen. Wir haben die gew\u00fcnschte Flexibilit\u00e4t geschaffen und gleichzeitig die API sauber und einfach gehalten. Neue Wrapper k\u00f6nnen auf dem UploadLayoutManager aufgebaut werden, um andere Dateitypen oder neue Layouts zu verarbeiten.<\/p>\n<\/div>\n<\/div><\/div><\/div><\/div><div id=\"pg-10330-21\"  class=\"panel-grid panel-has-style\" ><div class=\"panel-row-style panel-row-style-for-10330-21\" ><div id=\"pgc-10330-21-0\"  class=\"panel-grid-cell\" ><div id=\"panel-10330-21-0-0\" class=\"so-panel widget widget_sow-editor panel-first-child\" data-index=\"37\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-editor so-widget-sow-editor-base\"\n\t\t\t\n\t\t><h3 class=\"widget-title\">Zum Schluss<\/h3>\n<div class=\"siteorigin-widget-tinymce textwidget\">\n\t<p>Stell dir Bild-Uploader vor, die speziell f\u00fcr jedes Szenario entwickelt wurden, aber nur ein paar einfache Komponenten aus Pr\u00e4sentationsmarkup enthalten.  Sie k\u00f6nnen beide die gleiche Upload-Funktionalit\u00e4t nutzen, wenn es Sinn macht, aber mit einer v\u00f6llig anderen Pr\u00e4sentation. Oder du drehst die Idee um und verwendest die gleichen Uploader-Visualisierungen, aber mit v\u00f6llig unterschiedlichen API-Schnittstellen.<\/p>\n<p>Wie w\u00fcrdest du diese grundlegenden Komponenten noch verwenden? Welche anderen Uploader w\u00fcrdest du bauen? Der Himmel ist die Grenze, wenn du dir die Zeit nimmst, wiederverwendbare Komponenten zu bauen.<\/p>\n<\/div>\n<\/div><\/div><div id=\"panel-10330-21-0-1\" class=\"so-panel widget widget_sow-button-grid panel-last-child\" data-index=\"38\" ><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-button-grid so-widget-sow-button-grid-default-129192d28302-10330\"\n\t\t\t\n\t\t>\t<div class=\"sow-buttons-grid\">\n\t\t<div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-button so-widget-sow-button-flat-66466acb9e12\"\n\t\t\t\n\t\t><div class=\"ow-button-base ow-button-align-center\"\n>\n\t\t\t<a\n\t\t\t\t\thref=\"https:\/\/www.eventbrite.com\/engineering\/create-reusable-react-file-uploaders\/\"\n\t\t\t\t\tclass=\"sowb-button ow-icon-placement-left ow-button-hover\" target=\"_blank\" rel=\"noopener noreferrer\" \t>\n\t\t<span>\n\t\t\t\n\t\t\tFinde den Originalbeitrag bei Eventbrite Engineering\t\t<\/span>\n\t\t\t<\/a>\n\t<\/div>\n<\/div><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-button so-widget-sow-button-flat-66466acb9e12\"\n\t\t\t\n\t\t><div class=\"ow-button-base ow-button-align-center\"\n>\n\t\t\t<a\n\t\t\t\t\thref=\"https:\/\/reactnewsletter.com\/issues\/130\"\n\t\t\t\t\tclass=\"sowb-button ow-icon-placement-left ow-button-hover\" target=\"_blank\" rel=\"noopener noreferrer\" \t>\n\t\t<span>\n\t\t\t\n\t\t\tErschienen im React Newsletter Ausgabe #130\t\t<\/span>\n\t\t\t<\/a>\n\t<\/div>\n<\/div><div\n\t\t\t\n\t\t\tclass=\"so-widget-sow-button so-widget-sow-button-flat-66466acb9e12\"\n\t\t\t\n\t\t><div class=\"ow-button-base ow-button-align-center\"\n>\n\t\t\t<a\n\t\t\t\t\thref=\"https:\/\/react.statuscode.com\/issues\/102\"\n\t\t\t\t\tclass=\"sowb-button ow-icon-placement-left ow-button-hover\" target=\"_blank\" rel=\"noopener noreferrer\" \t>\n\t\t<span>\n\t\t\t\n\t\t\tErschienen in React Status Ausgabe #102\t\t<\/span>\n\t\t\t<\/a>\n\t<\/div>\n<\/div>\t<\/div>\n\t<\/div><\/div><\/div><\/div><\/div><\/div>","protected":false},"excerpt":{"rendered":"<p>Daniel wurde k\u00fcrzlich im Eventbrite-Engineering-Blog vorgestellt und wir sind sehr stolz auf die Arbeit, die er dort leistet. Das Event Creation Team bei Eventbrite brauchte einen React-basierten Bild-Uploader, der flexibel ist und gleichzeitig eine einfache Benutzeroberfl\u00e4che bietet. Die Komponenten des Bild-Uploaders sollten in einer Vielzahl von Szenarien als wiederverwendbare [...]<\/p>","protected":false},"author":3,"featured_media":13450,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wprm-recipe-roundup-name":"","wprm-recipe-roundup-description":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"default","adv-header-id-meta":"","stick-header-meta":"default","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"set","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[10,5,7],"tags":[85,106,112,168,175,199,238,292],"class_list":["post-10330","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-about-daniel","category-professional-opinions","category-technical-jargon","tag-component","tag-engineering","tag-eventbrite","tag-image-uploader","tag-javascript","tag-nashville","tag-react","tag-uploader"],"_links":{"self":[{"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/posts\/10330","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/comments?post=10330"}],"version-history":[{"count":12,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/posts\/10330\/revisions"}],"predecessor-version":[{"id":21449,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/posts\/10330\/revisions\/21449"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/media\/13450"}],"wp:attachment":[{"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/media?parent=10330"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/categories?post=10330"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/carterembry.com\/de\/wp-json\/wp\/v2\/tags?post=10330"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}