Existenzielle Reaktionsfragen und ein perfekter modaler Dialog
Jan 03, 2025 am 03:44 AMWas ist Ihrer Meinung nach das Komplizierteste an React? Neu rendern? Kontext? Portale? Parallelit?t?
Nein.
Der schwierigste Teil von React ist alles, was nicht-React drumherum ist. Die Antwort auf die Frage ?Wie funktionieren die oben aufgeführten Dinge?“ ist unkompliziert: Man muss lediglich dem Algorithmus folgen und sich Notizen machen. Das Ergebnis wird definitiv und immer dasselbe sein (wenn Sie es richtig nachverfolgen). Es sind nur Wissenschaft und Fakten.
Aber was ist mit ?Was macht eine Komponente gut?“ oder ?Wie setzt man … (etwas) richtig um?“ oder sogar ?Soll ich eine Bibliothek verwenden oder meine eigene L?sung erstellen?“ Die einzig sachlich richtige Antwort lautet hier: ?Es kommt darauf an.“ Es ist zuf?llig das am wenigsten hilfreiche.
Ich wollte für den neuen Artikel etwas Besseres finden. Aber da es für diese Art von Fragen keine einfachen Antworten und universellen L?sungen geben kann, entpuppte sich der Artikel eher als eine Art Durchlauf meines Denkprozesses und nicht als ?Das ist die Antwort, tu es immer.“ Ich hoffe, es ist immer noch nützlich.
Was ist also n?tig, um eine Funktion von einer Idee in eine produktionsreife L?sung umzuwandeln? Versuchen wir, einen einfachen modalen Dialog zu implementieren und zu sehen. Was kann daran m?glicherweise kompliziert sein? ?
Schritt 1: Beginnen Sie mit der einfachsten L?sung
Beginnen wir mit dem, was manchmal als ?Spike“ bezeichnet wird – der einfachsten m?glichen Implementierung, die dabei helfen kann, potenzielle L?sungen zu erkunden und weitere Anforderungen zu erfassen. Ich wei?, dass ich einen modalen Dialog umsetze. Nehmen wir an, ich habe ein hübsches Design wie dieses:
Ein Dialog ist im Grunde ein Element auf dem Bildschirm, das erscheint, wenn auf etwas wie eine Schaltfl?che geklickt wird. Genau da fange ich also an.
export default function Page() { const [isOpen, setIsOpen] = useState(false); return ( <> <button onClick={() => setIsOpen(true)}> Click me </button> {isOpen ? ( <div className="dialog">some content</div> ) : null} </> ); }
Status, eine Schaltfl?che, die auf Klicks wartet, und ein zukünftiger Dialog, der angezeigt wird, wenn der Status wahr ist. Dialog soll auch eine ?Schlie?en“-Aktion haben:
<button className="close-button" onClick={() => setIsOpen(false)} > Close </button>
Es hat auch einen ?Hintergrund“ – ein anklickbares halbtransparentes Div, das den Inhalt überlagert und beim Klicken das Verschwinden des Modals ausl?st.
<div className="backdrop" onClick={() => setIsOpen(false)} ></div>
Alle zusammen:
export default function Page() { const [isOpen, setIsOpen] = useState(false); return ( <> <button onClick={() => setIsOpen(true)}> Click me </button> {isOpen ? ( <> <div className="backdrop" onClick={() => setIsOpen(false)} ></div> <div className="dialog"> <button className="close-button" onClick={() => setIsOpen(false)} > Close </button> </div> </> ) : null} </> ); }
Normalerweise füge ich auch schon früh dezente Styles hinzu. Wenn ich sehe, dass die von mir implementierte Funktion auf dem Bildschirm genauso aussieht, wie sie sein soll, hilft mir das beim Nachdenken. Au?erdem kann es das Layout der Funktion beeinflussen, und genau das geschieht mit diesem Dialog.
Fügen wir schnell CSS für den Hintergrund hinzu – es ist nichts Besonderes, nur ein halbtransparenter Hintergrund auf einem Div mit Position: Fixed, das den gesamten Bildschirm einnimmt:
export default function Page() { const [isOpen, setIsOpen] = useState(false); return ( <> <button onClick={() => setIsOpen(true)}> Click me </button> {isOpen ? ( <div className="dialog">some content</div> ) : null} </> ); }
Der Dialog ist etwas interessanter, da er in der Mitte des Bildschirms positioniert werden muss. Es gibt natürlich 1001 M?glichkeiten, dies in CSS zu erreichen, aber meine liebste und wahrscheinlich einfachste ist diese:
<button className="close-button" onClick={() => setIsOpen(false)} > Close </button>
Wir verwenden eine ?feste“ Position, um den Layoutbeschr?nkungen zu entgehen, fügen 50 % nach links und oben hinzu, um das Div in die Mitte zu verschieben, und transformieren es um 50 % zurück. Der linke und der obere Teil werden relativ zum Bildschirm berechnet und die Transformation erfolgt relativ zur Breite/H?he des Div selbst, sodass es unabh?ngig von seiner Breite oder der Breite des Bildschirms genau in der Mitte erscheint.
Der letzte Teil des CSS in diesem Schritt besteht darin, den Dialog selbst und die Schaltfl?che ?Schlie?en“ richtig zu formatieren. Ich werde es hier nicht kopieren und einfügen, da die tats?chlichen Stile nicht so wichtig sind. Schauen Sie sich einfach das Beispiel an:
Schritt 2: Halten Sie inne, stellen Sie Fragen und denken Sie nach
Da ich nun eine grobe Implementierung der Funktion habe, ist es an der Zeit, sie ?real“ zu machen. Dazu müssen wir im Detail verstehen, was genau wir hier l?sen wollen und für wen. Technisch gesehen sollten wir uns darüber im Klaren sein, dass bevor etwas codiert wird, was h?ufig der Fall ist, dass dieser Schritt Schritt 1 sein sollte.
Ist dieser Dialog Teil eines Prototyps, der so schnell wie m?glich implementiert, den Investoren einmal gezeigt und nie wieder verwendet werden muss? Oder ist es vielleicht Teil einer generischen Bibliothek, die Sie auf npm und Open Source ver?ffentlichen m?chten? Oder ist es vielleicht Teil der Designsysteme, die Ihr Unternehmen mit 5.000 Mitarbeitern verwenden wird? Oder ist es Teil der internen Ausstattung Ihres kleinen 3-Personen-Teams und sonst nichts? Oder arbeiten Sie vielleicht für etwas wie TikTok und dieser Dialog wird Teil der Web-App sein, die nur auf Mobilger?ten verfügbar ist? Oder arbeiten Sie vielleicht für eine Agentur, die Apps nur für die Regierung schreibt?
Die Beantwortung dieser Fragen gibt die Richtung vor, was als n?chstes beim Codieren zu tun ist.
Wenn es sich nur um einen Prototyp zur einmaligen Verwendung handelt, ist er m?glicherweise bereits gut genug.
Wenn es als Teil einer Bibliothek als Open-Source-L?sung bereitgestellt werden soll, muss es über eine sehr gute Allzweck-API verfügen, die jeder Entwickler auf der Welt verwenden und verstehen kann, über zahlreiche Tests und eine gute Dokumentation.
Der Dialog, der Teil der Designsysteme einer Organisation mit 5.000 Personen ist, muss den Designrichtlinien der Organisation entsprechen und kann hinsichtlich der in das Repo eingebrachten externen Abh?ngigkeiten eingeschr?nkt sein. Daher müssen Sie m?glicherweise viele Dinge von Grund auf implementieren, anstatt npm install new-fancy-tool auszuführen.
Der Dialog einer Beh?rde, die für die Regierung arbeitet, muss wahrscheinlich der zug?nglichste und gesetzeskonformste Dialog im Universum sein. Andernfalls k?nnte die Agentur die Regierungsauftr?ge verlieren und bankrott gehen.
Und so weiter und so weiter.
Für den Zweck dieses Artikels gehen wir davon aus, dass der Dialog Teil einer neuen, derzeit laufenden Neugestaltung einer bestehenden gro?en kommerziellen Website mit t?glich Tausenden von Benutzern aus der ganzen Welt ist. Die Neugestaltung ist so weit fortgeschritten, dass der einzige Entwurf mit dem Dialog, den ich habe, dieser ist:
Der Rest kommt sp?ter, die Designer sind überfordert. Au?erdem bin ich Teil des festen Teams, das die Neugestaltung durchführt und die Website in Zukunft pflegt, und kein externer Auftragnehmer, der für ein einzelnes Projekt beauftragt wird.
In diesem Fall verschafft mir allein dieses Bild und das Wissen um das Ziel unseres Unternehmens genügend Informationen, um vernünftige Annahmen zu treffen und 90 % des Dialogs umzusetzen. Der Rest der 10 % kann sp?ter noch feinabgestimmt werden.
Das sind die Annahmen, die ich auf Grundlage der oben genannten Informationen treffen kann:
Die bestehende Website hat t?glich Tausende von Benutzern aus der ganzen Welt, daher muss ich sicherstellen, dass der Dialog zumindest sowohl auf gro?en und mobilen Bildschirmen als auch auf verschiedenen Browsern funktioniert. Im Idealfall muss ich vorhandene Analysen überprüfen, um absolut sicher zu sein, aber das ist eine ziemlich sichere Sache.
Mehr als ein Entwickler schreibt dafür Code, und der Code wird bleiben. Die Website ist gro? und hat bereits Tausende von Benutzern; Es handelt sich nicht um einen schnellen Prototyp für die Investoren. Ich muss also sicherstellen, dass der Code lesbar ist, die API Sinn macht, benutzbar und wartbar ist und keine offensichtlichen Fu?waffen enth?lt.
Das Unternehmen legt Wert auf sein Image und die Qualit?t seiner Website – warum sollte es sonst überhaupt ein Redesign durchführen? (Nehmen wir hier eine positive Absicht an?). Das bedeutet, dass ein gewisses Ma? an Qualit?t erwartet wird und ich vorausschauend denken und g?ngige Szenarien und Grenzf?lle antizipieren muss, auch wenn sie noch nicht Teil des aktuellen Designs sind.
Viele Benutzer meinen wahrscheinlich, dass nicht alle ausschlie?lich die Maus verwenden, um mit der Website zu interagieren. Der Dialog muss auch über Tastaturinteraktionen und m?glicherweise sogar unterstützende Technologien wie Bildschirmleseprogramme verfügbar sein.
Eine gro?e vorhandene Codebasis (es handelt sich um eine Neugestaltung, denken Sie daran!) bedeutet, dass es wahrscheinlich Einschr?nkungen hinsichtlich der externen Abh?ngigkeiten gibt, die ich für diese Funktion mitbringen kann. Jede externe Abh?ngigkeit hat ihren Preis, insbesondere bei gro?en und alten Codebasen. Für den Zweck des Artikels gehen wir davon aus, dass ich eine externe Bibliothek nutzen kann, dafür br?uchte ich aber eine gute Begründung.
Endlich kommen weitere Designs, daher muss ich vorhersehen, in welche Richtung es aus Design- und Benutzersicht gehen kann, und sicherstellen, dass der Code frühzeitig damit umgehen kann.
Schritt 3: Konsolidieren Sie die Modal Dialog API
Da ich nun die Anforderungen kenne und vernünftige Vermutungen habe, kann ich die eigentliche Dialogkomponente erstellen. Zun?chst einmal aus diesem Code:
export default function Page() { const [isOpen, setIsOpen] = useState(false); return ( <> <button onClick={() => setIsOpen(true)}> Click me </button> {isOpen ? ( <div className="dialog">some content</div> ) : null} </> ); }
Ich muss den Dialogteil unbedingt in eine wiederverwendbare Komponente extrahieren – es müssen viele dialogbasierte Funktionen implementiert werden.
<button className="close-button" onClick={() => setIsOpen(false)} > Close </button>
Der Dialog verfügt über eine onClose-Requisite – sie benachrichtigt die übergeordnete Komponente, wenn auf die Schaltfl?che ?Schlie?en“ oder den Hintergrund geklickt wird. Die übergeordnete Komponente hat dann immer noch den Status und stellt den Dialog wie folgt dar:
<div className="backdrop" onClick={() => setIsOpen(false)} ></div>
Jetzt schauen wir uns noch einmal das Design an und denken etwas mehr über Dialoge nach:
Es wird offensichtlich einen ?Fu?zeilen“-Teil des Dialogs mit Aktionsschaltfl?chen geben. H?chstwahrscheinlich wird es viele Variationen dieser Schaltfl?chen geben – eine, zwei, drei, linksbündig, rechtsbündig, mit Leerzeichen dazwischen usw. Au?erdem hat dieser Dialog keine Kopfzeile, aber Es ist sehr, sehr wahrscheinlich, dass dies der Fall ist – Dialoge mit einigen Headern sind ein ziemlich h?ufiges Muster. Hier wird es auf jeden Fall einen Inhalts-Bereich mit v?llig zuf?lligen Inhalten geben – von reinem Best?tigungstext über Formulare und interaktive Erlebnisse bis hin zu sehr langen scrollbaren ?Allgemeinen Gesch?ftsbedingungen“-Texten, die niemand liest.
Zum Schluss noch die Gr??e. Der Dialog im Design ist winzig, nur ein Best?tigungsdialog. Gro?e Formulare oder lange Texte passen dort nicht hin. Angesichts der Informationen, die wir in Schritt 2 gesammelt haben, kann man also mit ziemlicher Sicherheit davon ausgehen, dass die Gr??e des Dialogs ge?ndert werden muss. Da die Designer wahrscheinlich Designrichtlinien haben, k?nnen wir derzeit davon ausgehen, dass wir drei Variationen des Dialogs haben werden: ?klein“, ?mittel“ und ?gro?“.
All dies bedeutet, dass wir Requisiten für den ModalDialog ben?tigen: Fu?zeile und Kopfzeile sind nur normale Requisiten, die ReactNode akzeptieren, die Gr??e ist nur eine Vereinigung von Zeichenfolgen und der Inhaltsbereich wird als Hauptteil verwendet Kinder:
export default function Page() { const [isOpen, setIsOpen] = useState(false); return ( <> <button onClick={() => setIsOpen(true)}> Click me </button> {isOpen ? ( <> <div className="backdrop" onClick={() => setIsOpen(false)} ></div> <div className="dialog"> <button className="close-button" onClick={() => setIsOpen(false)} > Close </button> </div> </> ) : null} </> ); }
Wir steuern die Gr??e des Dialogs mit einem zus?tzlichen Klassennamen, der von den Requisiten stammt. Im wirklichen Leben h?ngt es jedoch stark von der Styling-L?sung ab, die im Repo verwendet wird.
Allerdings ist der Dialog in dieser Variante einfach zu flexibel – so ziemlich alles kann überall hingehen. In der Fu?zeile zum Beispiel k?nnen wir meistens nur ein oder zwei Schaltfl?chen erwarten, mehr nicht. Und diese Schaltfl?chen müssten überall auf der Website einheitlich angeordnet sein. Wir brauchen einen Wrapper, der sie ausrichtet:
.backdrop { background: rgba(0, 0, 0, 0.3); position: fixed; top: 0; left: 0; width: 100%; height: 100%; }
Das Gleiche gilt für den Inhalt – zumindest br?uchte er etwas Polsterung rundherum und die M?glichkeit zum Scrollen. Und die Kopfzeile ben?tigt m?glicherweise einige Stile für den Text. Das Layout sieht also so aus:
export default function Page() { const [isOpen, setIsOpen] = useState(false); return ( <> <button onClick={() => setIsOpen(true)}> Click me </button> {isOpen ? ( <div className="dialog">some content</div> ) : null} </> ); }
Aber leider k?nnen wir das nicht garantieren. Es ist sehr wahrscheinlich, dass irgendwann jemand mehr in der Fu?zeile haben m?chte als nur Schaltfl?chen. Oder einige der Dialoge müssten einen Header auf einem verkauften Hintergrund haben. Oder manchmal ben?tigt der Inhalt keine Polsterung.
Was ich damit meine, ist, dass wir eines Tages in der Lage sein müssen, den Kopf-/Inhalts-/Fu?zeilenteil zu formatieren. Und wahrscheinlich früher als erwartet.
Wir k?nnten diese Konfiguration natürlich einfach mit Requisiten übergeben und so etwas wie HeaderClassName-, ContentClassName- und FooterClassName-Requisiten haben. Und in einigen F?llen k?nnte es tats?chlich in Ordnung sein. Aber für so etwas wie den netten Dialog für das sch?ne Redesign k?nnten wir es besser machen.
Eine wirklich gute M?glichkeit, dieses Problem zu l?sen, besteht darin, unsere Kopf-/Inhalts-/Fu?zeile in eigene Komponenten zu extrahieren, wie folgt:
<button className="close-button" onClick={() => setIsOpen(false)} > Close </button>
und setzen Sie den ModalDialog-Code auf den Code ohne die Wrapper zurück:
<div className="backdrop" onClick={() => setIsOpen(false)} ></div>
Auf diese Weise würde ich in der übergeordneten App, wenn ich das Standarddesign für die Dialogteile haben m?chte, diese winzigen Komponenten verwenden:
export default function Page() { const [isOpen, setIsOpen] = useState(false); return ( <> <button onClick={() => setIsOpen(true)}> Click me </button> {isOpen ? ( <> <div className="backdrop" onClick={() => setIsOpen(false)} ></div> <div className="dialog"> <button className="close-button" onClick={() => setIsOpen(false)} > Close </button> </div> </> ) : null} </> ); }
Und wenn ich etwas v?llig Benutzerdefiniertes haben wollte, würde ich eine neue Komponente mit ihren eigenen benutzerdefinierten Stilen implementieren, ohne mit dem ModalDialog selbst herumzuspielen:
.backdrop { background: rgba(0, 0, 0, 0.3); position: fixed; top: 0; left: 0; width: 100%; height: 100%; }
Im übrigen brauche ich nicht einmal mehr die Kopf- und Fu?zeilen-Requisite. Ich kann den DialogHeader und den DialogFooter einfach an die Kinder weitergeben, den ModalDialog noch mehr vereinfachen und eine noch sch?nere API mit dem gleichen Ma? an Flexibilit?t haben, w?hrend ich überall ein einheitliches Design habe.
Die übergeordnete Komponente sieht dann so aus:
.dialog { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); }
Und die API des Dialogs sieht so aus:
export default function Page() { const [isOpen, setIsOpen] = useState(false); return ( <> <button onClick={() => setIsOpen(true)}> Click me </button> {isOpen ? ( <> <div className="backdrop" onClick={() => setIsOpen(false)} ></div> <div className="dialog"> <button className="close-button" onClick={() => setIsOpen(false)} > Close </button> </div> </> ) : null} </> ); }
Ich bin bisher ziemlich zufrieden damit. Es ist flexibel genug, um das Design beliebig zu erweitern, aber auch klar und sinnvoll genug, um problemlos eine einheitliche Benutzeroberfl?che in der gesamten App zu implementieren.
Hier ist das Live-Beispiel zum Herumspielen:
Schritt 4: Leistung und erneutes Rendern
Jetzt, da die Modal-API in einem ausreichend guten Zustand ist, ist es an der Zeit, sich mit der offensichtlichen Fu?waffe zu befassen, die ich implementiert habe. Wenn Sie genug von meinen Artikeln gelesen haben, haben Sie wahrscheinlich laut geschrien: ?Was machst du??? Neu rendern!!“ für die letzten zehn Minuten? Und natürlich hast du recht:
const ModalDialog = ({ onClose }) => { return ( <> <div className="backdrop" onClick={onClose}></div> <div className="dialog"> <button className="close-button" onClick={onClose}> Close </button> </div> </> ); };
Die Seitenkomponente hier hat den Status. Jedes Mal, wenn das Modal ge?ffnet oder geschlossen ist, ?ndert sich der Zustand und es kommt zu einem erneuten Rendern der gesamten Komponente und aller darin enthaltenen Elemente. Ja, ?vorzeitige Optimierung ist die Wurzel allen übels“ und ja, optimieren Sie die Leistung nicht, bevor Sie sie tats?chlich gemessen haben. In diesem Fall k?nnen wir die g?ngige Meinung getrost ignorieren.
Aus zwei Gründen. Erstens wei? ich mit Sicherheit, dass in der App viele Modalit?ten verstreut sein werden. Es handelt sich nicht um eine einmalige versteckte Funktion, die niemand nutzen wird. Daher ist die Wahrscheinlichkeit, dass jemand mit einer API wie dieser einen Zustand an eine Stelle setzt, an der er nicht sein sollte, recht hoch. Und zweitens erfordert es nicht viel Zeit und Mühe, um zu verhindern, dass das Problem des erneuten Renderns überhaupt auftritt. Nur 1 Minute Aufwand, und wir müssen hier überhaupt nicht an die Leistung denken.
Alles, was wir tun müssen, ist, den Zustand zu kapseln und die Idee einer ?unkontrollierten Komponente“ einzuführen:
export default function Page() { const [isOpen, setIsOpen] = useState(false); return ( <> <button onClick={() => setIsOpen(true)}> Click me </button> {isOpen ? ( <div className="dialog">some content</div> ) : null} </> ); }
Wobei der BaseModalDialog genau derselbe Dialog ist, den wir zuvor hatten, ich habe ihn nur umbenannt.
Und dann übergeben Sie eine Komponente, die den Dialog ausl?sen soll, als Trigger-Requisite:
<button className="close-button" onClick={() => setIsOpen(false)} > Close </button>
Die Seitenkomponente sieht dann folgenderma?en aus:
<div className="backdrop" onClick={() => setIsOpen(false)} ></div>
Kein Status innerhalb der Seite mehr, keine potenziell gef?hrlichen erneuten Renderings mehr.
Eine solche API sollte 95 % der Anwendungsf?lle abdecken, da ein Benutzer in den meisten F?llen auf etwas klicken muss, damit der Dialog angezeigt wird. In seltenen Situationen, in denen ein Dialog unabh?ngig angezeigt werden muss, beispielsweise auf einer Verknüpfung oder als Teil des Onboardings, kann ich trotzdem den BaseModalDialog verwenden und den Status manuell bearbeiten.
Schritt 5: Umgang mit Randf?llen und Zug?nglichkeit
Die API der ModalDialog-Komponente ist aus der React-Perspektive ziemlich solide, aber die Arbeit ist noch lange nicht erledigt. Angesichts der Must-Haves, die ich in Schritt 2 gesammelt habe, muss ich noch ein paar weitere Probleme beheben.
Problem 1: Ich binde den Trigger in einen zus?tzlichen Bereich ein – in bestimmten F?llen kann das das Layout einer Seite besch?digen. Ich muss die Hülle irgendwie loswerden.
Problem 2: Wenn ich den Dialog innerhalb eines Elements rendere, das einen neuen Stapelkontext erstellt, wird das Modal unter einigen Elementen angezeigt. Ich muss es innerhalb eines Portals rendern, nicht direkt im Layout, wie ich es jetzt tue.
Problem 3: Der Tastaturzugriff ist derzeit ziemlich schlecht. Wenn ein ordnungsgem?? implementierter modaler Dialog ge?ffnet wird, sollte der Fokus nach innen springen. Wenn es geschlossen ist, sollte der Fokus auf das Element zurückkehren, das den Dialog ausgel?st hat. Wenn der Dialog ge?ffnet ist, sollte der Fokus im Inneren ?gefangen“ sein und die Elemente au?erhalb sollten nicht fokussierbar sein. Durch Drücken der ESC-Taste sollte der Dialog geschlossen werden. Nichts davon ist derzeit umgesetzt.
Die Probleme 1 und 2 sind etwas ?rgerlich, k?nnen aber relativ schnell gel?st werden. Problem 3 ist jedoch manuell sehr mühsam zu erledigen. Au?erdem handelt es sich sicherlich um ein gel?stes Problem – jeder Dialog überall auf der Welt würde diese Funktionalit?t ben?tigen.
Die Kombination ?riesiger Aufwand, den ich selbst erledigen muss“ ?Sieht nach einem sicherlich gel?sten Problem aus“ ist der Punkt, an dem ich nach einer bestehenden Bibliothek suchen würde.
In Anbetracht all der Vorarbeiten, die ich bereits geleistet habe, ist es jetzt einfach, das Richtige auszuw?hlen.
Ich k?nnte auf alle vorhandenen UI-Komponentenbibliotheken wie Ant Design oder Material UI zurückgreifen und von dort aus einen Dialog verwenden. Aber wenn die Neugestaltung sie nicht nutzt, wird die Anpassung ihrer Designs an die, die ich brauche, mehr ?rger mit sich bringen als sie l?sen. Daher ist es für diesen Fall ein sofortiges NEIN.
Ich k?nnte eine der ?kopflosen“ UI-Bibliotheken wie Radix oder React Aria verwenden. Diese implementieren die Funktionalit?t wie Status und Trigger sowie die gesamte Zug?nglichkeit, überlassen das Design jedoch dem Verbraucher. Wenn ich mir ihre API ansehe, müsste ich noch einmal überprüfen, ob sie mir erm?glichen, den Status des Dialogs zu steuern, wenn ich es wirklich brauche, für die F?lle, in denen ich den Dialog manuell ausl?sen m?chte (was sie tun).
Wenn ich die Headless-Bibliotheken aus irgendeinem Grund nicht verwenden kann, würde ich zumindest versuchen, eine Bibliothek zu verwenden, die die Focus-Trap-Funktionalit?t übernimmt.
Nehmen wir für den Artikel an, dass ich jede Bibliothek mitbringen kann, die ich m?chte. In diesem Fall w?hle ich Radix – es ist sehr einfach zu verwenden und die API des Dialogs sieht dem, was ich bereits implementiert habe, sehr ?hnlich, sodass die Umgestaltung ein Kinderspiel sein sollte.
Wir müssten die API des Dialogs selbst ein wenig ?ndern:
export default function Page() { const [isOpen, setIsOpen] = useState(false); return ( <> <button onClick={() => setIsOpen(true)}> Click me </button> {isOpen ? ( <div className="dialog">some content</div> ) : null} </> ); }
Es ist so ziemlich das Gleiche wie vorher. Nur verwende ich anstelle von Divs überall Radix-Primitive.
Die unkontrollierte Dialognutzung ?ndert sich überhaupt nicht:
<button className="close-button" onClick={() => setIsOpen(false)} > Close </button>
Und der kontrollierte Dialog ?ndert sich geringfügig – ich müsste ihm Requisiten übergeben, anstatt bedingtes Rendering:
<div className="backdrop" onClick={() => setIsOpen(false)} ></div>
Sehen Sie sich das Beispiel unten an und versuchen Sie, die Tastatur zum Navigieren zu verwenden. Alles funktioniert so, wie ich es brauche, wie cool ist das?
Als Bonus kümmert sich Radix auch um das Portal-Problem und schlie?t Trigger nicht in eine Spanne ein. Ich muss keine Randf?lle mehr l?sen, daher kann ich mit dem letzten Schritt fortfahren.
Schritt 6: Endpolitur
Die Funktion ist noch nicht fertig! ? Der Dialog sieht jetzt ziemlich solide aus und fühlt sich auch so an, daher werde ich zu diesem Zeitpunkt keine wesentlichen ?nderungen an seiner Implementierung vornehmen. Aber es bedarf noch einiger Dinge, um als ?perfekter“ Dialog für den Anwendungsfall, den ich l?se, zu gelten.
Eins: Das allererste, worum die Designer mich bitten werden, falls sie es noch nicht getan haben, ist das Hinzufügen einer subtilen Animation für das ?ffnen des Dialogs. Ich müsste es vorhersehen und mich daran erinnern, wie man Animationen in React erstellt.
Zwei: Ich müsste dem Dialog maximale Breite und maximale H?he hinzufügen, damit er auf kleinen Bildschirmen immer noch anst?ndig aussieht. Und überlegen Sie, wie es auf sehr gro?en Bildschirmen aussehen würde.
Drei: Ich müsste mit den Designern darüber sprechen, wie sich der Dialog auf Mobilger?ten verhalten soll. Die Chancen stehen gut, dass sie mich bitten werden, daraus ein Einschubfenster zu machen, das unabh?ngig von der Gr??e des Dialogs den gr??ten Teil des Bildschirms einnimmt.
Vier: Ich müsste mindestens die Komponenten DialogTitle und DialogDescription einführen – Radix wird Sie aus Gründen der Barrierefreiheit bitten, sie zu verwenden.
Fünf: Tests! Der Dialog bleibt bestehen und wird von anderen Personen aufrechterhalten, daher sind Tests in diesem Fall so gut wie obligatorisch.
Und wahrscheinlich noch jede Menge andere kleine Dinge, die ich jetzt vergessen habe und die sp?ter auftauchen werden. Ganz zu schweigen von der Umsetzung der eigentlichen Designs für den Inhalt des Dialogs.
Noch ein paar Gedanken
Wenn Sie den ?Dialog“ oben durch ?SomeNewFeature“ ersetzen, ist dies mehr oder weniger der Algorithmus, den ich verwende, um so ziemlich alles Neue zu implementieren.
Schnelle ?Spitze“ der L?sung(en) → Anforderungen für die Funktion sammeln → dafür sorgen, dass sie funktioniert → sie leistungsf?hig machen → sie vervollst?ndigen → sie perfekt machen.
Für so etwas wie den eigentlichen Dialog, den ich mittlerweile hunderte Male umgesetzt habe, mache ich den ersten Schritt in 10 Sekunden im Kopf und beginne gleich mit Schritt 2.
Für etwas sehr Kompliziertes und Unbekanntes kann Schritt 1 l?nger dauern und das sofortige Erkunden verschiedener L?sungen und Bibliotheken erfordern.
Etwas, das nicht ganz unbekannt ist, sondern nur eine ?normale Funktion, die wir ausführen müssen“, k?nnte Schritt 1 überspringen, da es m?glicherweise nichts zu erkunden gibt.
Sehr oft, insbesondere in ?agilen“ Umgebungen, wird es eher eine Spirale als eine gerade Linie sein, in der Anforderungen schrittweise bereitgestellt werden und sich oft ?ndern, und wir kehren regelm??ig zu den ersten beiden Schritten zurück.
Ich hoffe, diese Art von Artikel war nützlich! ?? Lassen Sie mich wissen, ob Sie mehr Inhalte wie diesen haben m?chten oder lieber das übliche ?Wie die Dinge funktionieren“-Zeug bevorzugen.
Und ich freue mich darauf zu h?ren, wie sich dieser Prozess in euren K?pfen unterscheidet?
Ursprünglich ver?ffentlicht unter https://www.developerway.com. Auf der Website gibt es noch mehr Artikel wie diesen ?
Schauen Sie sich das Advanced React-Buch an, um Ihr React-Wissen auf die n?chste Stufe zu bringen.
Abonnieren Sie den Newsletter, verbinden Sie sich auf LinkedIn oder folgen Sie auf Twitter, um benachrichtigt zu werden, sobald der n?chste Artikel erscheint.
Und übrigens noch eine letzte Sache: Wenn Sie bald mit einem neuen Projekt beginnen und keinen Designer und die Zeit haben, das Designerlebnis wie beschrieben zu verfeinern – ich habe vor kurzem Stunden um Stunden (und Stunden) damit verbracht, ein neues Projekt zu implementieren Bibliothek von UI-Komponenten für diesen Fall. Es verfügt über kopier- und einfügbare Komponenten und g?ngige Muster, Radix und Tailwind, Dunkelmodus, Barrierefreiheit und sofort einsatzbereite mobile Unterstützung. Einschlie?lich des perfekten modalen Dialogs oben! ?
Probieren Sie es aus: https://www.buckets-ui.com/
Das obige ist der detaillierte Inhalt vonExistenzielle Reaktionsfragen und ein perfekter modaler Dialog. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Hei?e KI -Werkzeuge

Undress AI Tool
Ausziehbilder kostenlos

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem v?llig kostenlosen KI-Gesichtstausch-Tool aus!

Hei?er Artikel

Hei?e Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Hei?e Themen

Java und JavaScript sind unterschiedliche Programmiersprachen, die jeweils für verschiedene Anwendungsszenarien geeignet sind. Java wird für die Entwicklung gro?er Unternehmen und mobiler Anwendungen verwendet, w?hrend JavaScript haupts?chlich für die Entwicklung von Webseiten verwendet wird.

JavaScriptComents AreseessentialFormaintaining, Lesen und GuidingCodeexexecution.1) einzelne Linecommments Arequickickexplanationen.2) Multi-LindexplainComproxlogicorProvedetailedDocumentation.3) InlinecommentsclarifyspecificPartsosensofCode.BestPracticic

Die folgenden Punkte sollten bei der Verarbeitung von Daten und Zeiten in JavaScript festgestellt werden: 1. Es gibt viele M?glichkeiten, Datumsobjekte zu erstellen. Es wird empfohlen, ISO -Format -Zeichenfolgen zu verwenden, um die Kompatibilit?t sicherzustellen. 2. Die Zeitinformationen erhalten und festlegen k?nnen und setzen Sie Methoden fest, und beachten Sie, dass der Monat mit 0 beginnt. 3. Die manuell formatierende Daten sind Zeichenfolgen erforderlich, und auch Bibliotheken von Drittanbietern k?nnen verwendet werden. 4. Es wird empfohlen, Bibliotheken zu verwenden, die Zeitzonen wie Luxon unterstützen. Das Beherrschen dieser wichtigen Punkte kann h?ufige Fehler effektiv vermeiden.

PlatztagsattheBottomofabogpostorwebpageServeSpracticalPurposesforseo, Usexperience und design.1ithelpswithseobyallowingEnginestoaccessKeyword-relevantTagswithoutClutteringHemainContent.2.

JavaScriptispreferredforwebdevelopment,whileJavaisbetterforlarge-scalebackendsystemsandAndroidapps.1)JavaScriptexcelsincreatinginteractivewebexperienceswithitsdynamicnatureandDOMmanipulation.2)Javaoffersstrongtypingandobject-orientedfeatures,idealfor

JavaScripthassevenfundamentaldatatypes:number,string,boolean,undefined,null,object,andsymbol.1)Numbersuseadouble-precisionformat,usefulforwidevaluerangesbutbecautiouswithfloating-pointarithmetic.2)Stringsareimmutable,useefficientconcatenationmethodsf

Ereigniserfassung und Blase sind zwei Phasen der Ereignisausbreitung in DOM. Die Erfassung erfolgt von der oberen Schicht bis zum Zielelement, und die Blase ist vom Zielelement bis zur oberen Schicht. 1. Die Ereigniserfassung wird implementiert, indem der UseCapture -Parameter von AddEventListener auf true festgelegt wird. 2. Ereignisblase ist das Standardverhalten, Uscapture ist auf false oder weggelassen. 3. Die Ereignisausbreitung kann verwendet werden, um die Ereignisausbreitung zu verhindern. 4. Event Bubbling unterstützt die Ereignisdelegation, um die Effizienz der dynamischen Inhaltsverarbeitung zu verbessern. 5. Capture kann verwendet werden, um Ereignisse im Voraus abzufangen, wie z. B. Protokollierung oder Fehlerverarbeitung. Das Verst?ndnis dieser beiden Phasen hilft dabei, das Timing und die Reaktion von JavaScript auf Benutzeroperationen genau zu steuern.

Java und JavaScript sind verschiedene Programmiersprachen. 1.Java ist eine statisch typisierte und kompilierte Sprache, die für Unternehmensanwendungen und gro?e Systeme geeignet ist. 2. JavaScript ist ein dynamischer Typ und eine interpretierte Sprache, die haupts?chlich für die Webinteraktion und die Front-End-Entwicklung verwendet wird.
