Ich wollte in einem meiner Projekte eine Benachrichtigungsnachricht implementieren, ähnlich wie man sie in Google Docs sieht, während ein Dokument gespeichert wird. Mit anderen Worten: Jedes Mal, wenn eine Änderung vorgenommen wird, erscheint eine Nachricht, die anzeigt, dass das Dokument gespeichert wird. Dann, sobald die Änderungen gespeichert sind, wird die Nachricht zu: „Alle Änderungen in Drive gespeichert.“
Werfen wir einen Blick darauf, wie wir das mit einem booleschen Wert machen könnten, aber tatsächlich drei mögliche Zustände abdecken. Dies ist definitiv nicht der einzige Weg, dies zu tun, und ehrlich gesagt bin ich mir nicht einmal sicher, ob es der beste Weg ist. So oder so, es hat für mich funktioniert.
Hier ist ein Beispiel für diesen „Speichervorgang…“-Status

…und der „Gespeichert“-Status

Die Verwendung eines Boolean-Wertes zur Definition des Zustands war meine sofortige Reaktion. Ich könnte eine Variable namens isSaving haben und diese verwenden, um eine bedingte Zeichenkette in meiner Vorlage zu rendern, wie hier
let isSaving;
…und in der Vorlage
<span>{{ isSaving ? ‘Saving...’ : ‘All changes saved’ }}</span>
Jetzt setzen wir den Wert jedes Mal auf true, wenn wir mit dem Speichern beginnen, und dann auf false, wenn kein Speichervorgang läuft. Einfach, oder?
Hier gibt es jedoch ein Problem, und es handelt sich um ein kleines UX-Problem. Die *Standardnachricht* wird als „Alle Änderungen gespeichert“ gerendert. Wenn der Benutzer die Seite *anfänglich* aufruft, findet kein Speichervorgang statt, und wir erhalten die Nachricht „Gespeichert“, obwohl nie ein Speichervorgang stattgefunden hat. Ich würde es vorziehen, nichts anzuzeigen, bis die erste Änderung die erste Nachricht „Speichervorgang…“ auslöst.
Dies erfordert einen **dritten Status** in unserer Variablen: isSaving. Nun stellt sich die Frage: Ändern wir den Wert in eine *Zeichenkette* als einen der drei Zustände? Das könnten wir tun, aber was, wenn wir den dritten Zustand in unserer aktuellen booleschen Variablen selbst erreichen könnten?
isSaving kann zwei Werte annehmen: true oder false. Aber was ist der Wert *direkt* nachdem wir ihn in der Anweisung deklariert haben: let isSaving;? Es ist undefined, weil der Wert jeder Variablen undefined ist, wenn sie deklariert wird, es sei denn, ihr wird etwas zugewiesen. Großartig! Wir können diesen anfänglichen undefined-Wert zu unserem Vorteil nutzen… aber das erfordert eine leichte Änderung daran, wie wir unsere Bedingung in der Vorlage schreiben.
Der ternäre Operator, den wir verwenden, ergibt den zweiten Ausdruck für alles, was nicht zu true konvertiert werden kann. Die Werte undefined und false sind beide nicht true und werden daher für den ternären Operator als false ausgewertet. Selbst eine if/else-Anweisung würde ähnlich funktionieren, da else für alles ausgewertet wird, was nicht true ist. Aber wir möchten zwischen undefined und false unterscheiden. Dies kann behoben werden, indem explizit auch auf den false-Wert geprüft wird, wie hier
<span>
{{ isSaving === true ?
‘Saving...’ :
(isSaving === false ? ‘All changes saved’: ‘’)
}}
</span>
Wir prüfen jetzt strikt auf true und false-Werte. Dies hat unseren ternären Operator etwas verschachtelt und schwer lesbar gemacht. Wenn unsere Vorlage if/else-Anweisungen unterstützt, können wir die Vorlage wie folgt umgestalten:
<span>
{% if isSaving === true %}
Saving...
{% elseif isSaving === false %}
All changes saved
{% endif %}
</span>
Aha! Nichts wird gerendert, wenn die Variable weder true noch false ist – genau das, was wir wollen!
Das bedeutet, Sie könnten auch
null,0,"",'asdf'oder alles außertrueundfalseverwenden, um Zustände anzuzeigen. In einigen Fällen würde ich numerische Werte bevorzugen, die in benannten Konstanten gespeichert sind, um benutzerdefinierte Zustände zur Lesbarkeit anzugeben.Ich denke, es wäre besser, einfach 3 Status zu verwenden und diese entweder als Zeichenketten oder als ganze Zahlen zu speichern.
1 – GELADEN
2 – UNGESPEICHERT
3 – GESPEICHERT
Für mich würde ich bei asynchronen Interaktionen wie dieser eine Statusvariable namens saveState verwenden, die einen der folgenden Werte annehmen kann: ‚IDLE‘, ‚LOADING‘, ‚SUCCESS‘, ‚FAIL‘. Auf diese Weise könnten Sie fehlgeschlagene Anfragen behandeln
Ich stimme dem hier eher zu als dem Artikel. Undefiniert sollte mit undefinierten Semantiken verwendet werden. Wenn es für andere Zwecke verwendet wird, wird es verwirrend sein.
Ja, manchmal funktioniert es gut, und ich würde ‚undefined‘ mit einem ‚fehlenden Zustand‘ vergleichen, kein ‚isSaving‘ und keine Beschreibungen des Zustands „Speichervorgang…“
Sich auf eine nicht initialisierte Variable zu verlassen, würde ich nicht tun;
Es macht den Code schwerer verständlich, wenn man ihn liest.
Sie müssen bedenken, dass diese Variable jederzeit im Code, der sie umgibt, verändert werden kann, was die Reibung im System erhöht.
Typanalyse und -prüfung können verwirrt werden.
In Bezug auf den vorherigen Punkt: Ein kleiner Fehler in einer Laufzeitumgebung/einem Compiler in Bezug auf Hoisting führt zu einem anderen Verhalten.
Aber vor allem ist es ein Hinweis darauf, dass Sie möglicherweise etwas vermissen, das die Kopplung verringern und die Testbarkeit erhöhen würde. Ich würde zum Beispiel eine Art Warteschlange von zu speichernden Änderungen oder ein Repository erwarten, das ich auf ausstehende Operationen abfragen könnte.
Ich schlage vor, die drei booleschen Zustände sollten in True, False und Cat umbenannt werden. Ich bin sicher, dass eine solche Benennung bald populär verwendet würde.
Etwas einfacher zu lesen, wenn man sich daran gewöhnt hat
{{
isSaving === true
? „Speichervorgang…“
isSaving === false
? „Alle Änderungen gespeichert“
:
}}
Was für ein schlechtes Beispiel hier gegeben wird. Es gibt Abstraktionen, die die Anwesenheit oder Abwesenheit von Werten modellieren. Einige JS-Implementierungen: https://gcanti.github.io/fp-ts/modules/Option.ts.html https://folktale.origamitower.com/api/v2.3.0/en/folktale.maybe.html https://funfix.org/api/core/classes/option.html
Das ist ziemlich schlecht, wenn ich diesen Code in einem Projekt finden würde, an dem ich arbeiten würde, würde ich ihn sofort refaktorieren (nachdem ich versucht habe zu verstehen, was zur Hölle passiert). Erstens sollten boolesche Variablen nicht so verwendet werden, es ist verwirrend für andere, die den Code lesen, und ist buchstäblich ein Hack. Zweitens ist das Verschachteln von ternären Operatoren meiner Meinung nach auch ziemlich unordentlicher Code
Ternary-Operatoren müssen nicht hässlich sein. Es ist nur eine Frage der Formatierung. Zum Beispiel in JSX
Beachten Sie, dass PHP mit ternärer Verkettung nicht so gut umgehen kann.
Diese Implementierung hat ihre Grenzen. Was ist, wenn das Speichern fehlschlägt und ich die Statusmeldung aktualisieren möchte, um dies zu vermitteln?
Ich würde es vorziehen, hier definiertere Statuskonstanten zu verwenden.
Ja, ich stimme den anderen Beiträgen zu, dass dies ein Anti-Pattern ist. Der Versuch,
undefinedeine zusätzliche Bedeutung zu geben, schafft Raum für subtile Fehler. Es schafft implizites Wissen in Ihrem Code, dassundefinedtatsächlich „Anfangszustand“ bedeutet, im Gegensatz zu „Ich habe vergessen, diese Variable festzulegen“. Eine Zeichenkette macht die Variable zu einem konsistenten Typ, was die Typprüfung erleichtert, und macht den Wert der Variablen sofort klar, was sie darstellt, insbesondere in Fällen, in denen sie an andere Funktionen übergeben oder separat von dort gespeichert wird, wo sie verwendet wird. Ich würde die Zeichenketten wahrscheinlich in eine Konstante extrahieren.Auf diese Weise vermeiden Sie die bedingte Logik vollständig und können neue Zustände wie
errortrivial hinzufügen.