11 Tipps zur Verwendung von Redux bei der Entwicklung von React-Anwendungen

Bei der Entwicklung von React-Anwendungen sind kleine Projekte in Bezug auf die Codearchitektur häufig flexibler als große. Es ist nichts Falsches daran, solche Projekte anhand praktischer Richtlinien für größere Anwendungen zu erstellen. Bei kleinen Projekten kann dies jedoch einfach unnötig sein. Je kleiner die Anwendung ist, desto „herablassender“ bezieht sie sich auf die Verwendung einfacher Lösungen, die möglicherweise nicht optimal sind, aber nicht viel Zeit für ihre Implementierung benötigen.



Trotzdem möchte ich darauf hinweisen, dass einige der Empfehlungen, die in diesem Material gegeben werden, auf React-Anwendungen jeder Größenordnung abzielen.

Wenn Sie noch nie eine Produktionsanwendung erstellt haben, können Sie sich mit diesem Artikel auf die Entwicklung umfangreicher Lösungen vorbereiten. So etwas könnte sehr gut eines Ihrer nächsten Projekte werden. Das Schlimmste, was einem Programmierer passieren kann, ist, wenn er an einem Projekt arbeitet und erkennt, dass er große Mengen an Code umgestalten muss, um die Skalierbarkeit und Wartbarkeit der Anwendung zu verbessern. Alles sieht noch schlimmer aus, wenn es vor dem Refactoring keine Unit-Tests im Projekt gab.

Der Autor dieses Materials bittet den Leser, sein Wort dafür zu nehmen. Er war in ähnlichen Situationen. So bekam er mehrere Aufgaben, die in einer bestimmten Zeit gelöst werden mussten. Zuerst fand er alles, was er tat, ausgezeichnet. Die Quelle solcher Gedanken war, dass seine Webanwendung nach Änderungen weiter funktionierte und gleichzeitig schnell weiter funktionierte. Er wusste, wie man Redux benutzt und wie man eine normale Interaktion zwischen Benutzeroberflächenkomponenten herstellt. Es schien ihm, dass er die Konzepte von Reduzierern und Aktionen tief verstand. Er fühlte sich unverwundbar.

Aber hier hat sich die Zukunft eingeschlichen.

Nach einigen Monaten der Arbeit an der Anwendung wurden mehr als 15 neue Funktionen hinzugefügt. Danach geriet das Projekt außer Kontrolle. Der Code, der die Redux-Bibliothek verwendet hat, ist sehr schwer zu pflegen. Warum ist das passiert? War es zunächst nicht so, dass das Projekt ein langes und wolkenloses Leben erwartete?

Der Autor des Artikels sagt, dass er durch ähnliche Fragen festgestellt habe, dass er mit seinen eigenen Händen eine Zeitbombe in das Projekt gepflanzt habe.

Die Redux-Bibliothek hilft bei korrekter Verwendung in großen Projekten, wenn diese Projekte wachsen, ihren Code in einem unterstützten Zustand zu halten.

Hier sind 11 Tipps für diejenigen, die skalierbare React-Anwendungen mit Redux entwickeln möchten.

1. Platzieren Sie den Aktionscode und die Konstanten nicht an einer Stelle


Möglicherweise stoßen Sie auf einige Redux-Tutorials, in denen Konstanten und alle Aktionen an derselben Stelle platziert werden. Dieser Ansatz kann jedoch mit zunehmender Anwendung der Anwendung schnell zu Problemen führen. Konstanten müssen separat gespeichert werden, z. B. in ./src/constants . Um nach Konstanten zu suchen, müssen Sie daher nur einen Ordner und nicht mehrere anzeigen.

Darüber hinaus sieht die Erstellung separater Dateien zum Speichern von Aktionen völlig normal aus. Solche Dateien enthalten Aktionen, die in direktem Zusammenhang zueinander stehen. Aktionen in einer einzelnen Datei können beispielsweise Ähnlichkeiten hinsichtlich der Verwendung und Verwendung aufweisen.

Angenommen, Sie entwickeln eine Spielhalle oder ein Rollenspiel und erstellen die Klassen warrior (Krieger), sorceress (Zauberin) und archer (Bogenschütze). In einer solchen Situation können Sie ein hohes Maß an Codeunterstützung erreichen, indem Sie die Aktionen wie folgt organisieren:

 src/actions/warrior.js src/actions/sorceress.js src/actions/archer.js 

Es wird viel schlimmer sein, wenn alles in eine Datei fällt:

 src/actions/classes.js 

Wenn die Anwendung sehr groß wird, ist es möglicherweise noch besser, ungefähr die folgende Struktur der Code-Aufteilung in Dateien zu verwenden:

 src/actions/warrior/skills.js src/actions/sorceress/skills.js src/actions/archer/skills.js 

Hier ist nur ein kleines Fragment einer solchen Struktur gezeigt. Wenn Sie breiter denken und diesen Ansatz konsequenter anwenden, erhalten Sie so etwas wie diesen Satz von Dateien:

 src/actions/warrior/skills.js src/actions/warrior/quests.js src/actions/warrior/equipping.js src/actions/sorceress/skills.js src/actions/sorceress/quests.js src/actions/sorceress/equipping.js src/actions/archer/skills.js src/actions/archer/quests.js src/actions/archer/equipping.js 

So könnte die Aktion aus der Datei src/actions/sorceress/skills action src/actions/sorceress/skills für das sorceress Objekt aussehen:

 import { CAST_FIRE_TORNADO, CAST_LIGHTNING_BOLT } from '../constants/sorceress' export const castFireTornado = (target) => ({ type: CAST_FIRE_TORNADO, target, }) export const castLightningBolt = (target) => ({ type: CAST_LIGHTNING_BOLT, target, }) 

Hier ist der Inhalt der src/actions/sorceress/equipping :

 import * as consts from '../constants/sorceress' export const equipStaff = (staff, enhancements) => {...} export const removeStaff = (staff) => {...} export const upgradeStaff = (slot, enhancements) => { return (dispatch, getState, { api }) => {   //                 const state = getState()   const currentEquipment = state.classes.sorceress.equipment.current   const staff = currentEquipment[slot]   const isMax = staff.level >= 9   if (isMax) {     return   }   dispatch({ type: consts.UPGRADING_STAFF, slot })   api.upgradeEquipment({     type: 'staff',     id: currentEquipment.id,     enhancements,   })   .then((newStaff) => {     dispatch({ type: consts.UPGRADED_STAFF, slot, staff: newStaff })   })   .catch((error) => {     dispatch({ type: consts.UPGRADE_STAFF_FAILED, error })   }) } } 

Der Grund, warum wir den Code auf diese Weise organisieren, ist, dass Projekten ständig neue Funktionen hinzugefügt werden. Dies bedeutet, dass wir auf ihr Erscheinungsbild vorbereitet sein müssen und gleichzeitig sicherstellen müssen, dass die Dateien nicht mit Code überladen werden.

Zu Beginn der Arbeit an dem Projekt scheint dies unnötig zu sein. Aber je größer das Projekt wird, desto stärker wird die Stärke eines solchen Ansatzes spürbar.

2. Platzieren Sie den Reduziercode nicht an einer Stelle


Wenn ich sehe, dass der Code meiner Reduzierungen zu etwas ähnlichem wie dem unten gezeigten wird, verstehe ich, dass ich etwas ändern muss.

 const equipmentReducers = (state, action) => { switch (action.type) {   case consts.UPGRADING_STAFF:     return {       ...state,       classes: {         ...state.classes,         sorceress: {           ...state.classes.sorceress,           equipment: {             ...state.classes.sorceress.equipment,             isUpgrading: action.slot,           },         },       },     }   case consts.UPGRADED_STAFF:     return {       ...state,       classes: {         ...state.classes,         sorceress: {           ...state.classes.sorceress,           equipment: {             ...state.classes.sorceress.equipment,             isUpgrading: null,             current: {               ...state.classes.sorceress.equipment.current,               [action.slot]: action.staff,             },           },         },       },     }   case consts.UPGRADE_STAFF_FAILED:     return {       ...state,       classes: {         ...state.classes,         sorceress: {           ...state.classes.sorceress,           equipment: {             ...state.classes.sorceress.equipment,             isUpgrading: null,           },         },       },     }   default:     return state } } 

Ein solcher Code könnte zweifellos sehr schnell zu viel Chaos führen. Daher ist es am besten, die Struktur der Arbeit mit dem Staat in möglichst einfacher Form beizubehalten und auf das Mindestniveau ihrer Verschachtelung zu zielen. Sie können stattdessen versuchen, auf die Zusammensetzung der Reduzierungen zurückzugreifen.

Ein nützlicher Trick bei der Arbeit mit Reduzierern besteht darin, einen Reduzierer höherer Ordnung zu erstellen, den andere Reduzierer erzeugen. Lesen Sie hier mehr darüber.

3. Verwenden Sie informative Variablennamen


Das Benennen von Variablen scheint auf den ersten Blick eine elementare Aufgabe zu sein. Tatsächlich kann diese Aufgabe jedoch eine der schwierigsten sein.

Die Auswahl von Variablennamen ist im Allgemeinen für praktische Richtlinien zum Schreiben von sauberem Code relevant. Der Grund, warum es so etwas wie einen „Variablennamen“ im Allgemeinen gibt, ist, dass dieser Aspekt der Codeentwicklung in der Praxis eine sehr wichtige Rolle spielt. Die erfolglose Auswahl von Variablennamen ist ein sicherer Weg, sich selbst und Ihren Teammitgliedern in Zukunft Schaden zuzufügen.

Haben Sie jemals versucht, den Code eines anderen zu bearbeiten, und gleichzeitig Schwierigkeiten gehabt, zu verstehen, was genau dieser Code bewirkt? Haben Sie jemals ein ausländisches Programm durchgeführt und festgestellt, dass es nicht wie erwartet funktioniert?

Ich würde argumentieren, um zu beweisen, dass Sie in solchen Fällen auf den sogenannten "Dirty Code" gestoßen sind.

Wenn Sie in großen Anwendungen mit ähnlichem Code umgehen müssen, ist dies nur ein Albtraum. Leider passiert das ziemlich oft.

Hier ist ein Fall aus dem Leben. Ich habe den React-Hook-Code aus einer Anwendung heraus bearbeitet und in diesem Moment haben sie mir eine Aufgabe gesendet. In der Anwendung sollte die Möglichkeit implementiert werden, zusätzliche Informationen über Ärzte anzuzeigen. Diese Informationen sollten dem Patienten angezeigt worden sein, der auf das Profilbild des Arztes klickt. Es war notwendig, es aus der Tabelle zu nehmen, es musste zum Client gelangen, nachdem die nächste Anfrage an den Server verarbeitet worden war.

Diese Aufgabe war nicht schwierig. Das Hauptproblem bestand darin, dass ich zu viel Zeit damit verbringen musste, herauszufinden, wo genau sich das, was ich brauchte, im Projektcode befand.

Ich habe im Code nach den Wörtern info , dataToSend , dataObject und nach anderen Wörtern dataToSend , die meiner dataObject mit vom Server empfangenen Daten verknüpft sind. Nach 5-10 Minuten konnte ich den Code finden, der für die Arbeit mit den benötigten Daten verantwortlich war. Das Objekt, in dem sie paymentObject heißt paymentObject . Meiner Meinung nach kann ein Objekt, das sich auf Zahlungen bezieht, so etwas wie einen CVV-Code, eine Kreditkartennummer, eine Postleitzahl des Zahlers und andere ähnliche Informationen enthalten. Das Objekt, das ich entdeckte, hatte 11 Eigenschaften. Nur drei davon betrafen Zahlungen: Zahlungsmethode, Zahlungsprofil-ID und eine Liste der Gutscheincodes.

Die Situation hat sich auch nicht verbessert, da ich Änderungen an diesem Objekt vornehmen musste, die zur Lösung der vor mir liegenden Aufgabe erforderlich waren.

Kurz gesagt, es wird empfohlen, keine obskuren Namen für Funktionen und Variablen zu verwenden. Hier ist ein Beispielcode, in dem der Name der notify seine Bedeutung nicht preisgibt:

 import React from 'react' class App extends React.Component { state = { data: null } //  -? notify = () => {   if (this.props.user.loaded) {     if (this.props.user.profileIsReady) {       toast.alert(         'You are not approved. Please come back in 15 minutes or you will be deleted.',         {           position: 'bottom-right',           timeout: 15000,         },       )     }   } } render() {   return this.props.render({     ...this.state,     notify: this.notify,   }) } } export default App 

4. Ändern Sie keine Datenstrukturen oder -typen in bereits konfigurierten Anwendungsdatenströmen


Einer der größten Fehler, den ich jemals gemacht habe, war das Ändern der Datenstruktur in einem bereits konfigurierten Anwendungsdatenstrom. Die neue Datenstruktur würde eine enorme Leistungssteigerung bringen, da schnelle Methoden zum Suchen nach Daten in im Speicher gespeicherten Objekten verwendet wurden, anstatt über Arrays zu iterieren. Aber es war zu spät.

Ich bitte Sie, dies nicht zu tun. Vielleicht kann so etwas nur jemandem gewährt werden, der genau weiß, welche Teile der Anwendung davon betroffen sein können.

Was sind die Konsequenzen eines solchen Schrittes? Wenn beispielsweise etwas zuerst ein Array war und dann ein Objekt wurde, kann dies den Betrieb vieler Teile der Anwendung stören. Ich habe einen großen Fehler gemacht, als ich glaubte, dass ich mich an alle Stellen im Code erinnern konnte, die von einer Änderung in der Darstellung strukturierter Daten betroffen sein könnten. In solchen Fällen gibt es jedoch immer einen Code, der von der Änderung betroffen ist und an den sich niemand erinnert.

5. Verwenden Sie Schnipsel


Früher war ich ein Fan des Atom-Editors, wechselte aber zu VS Code, da dieser Editor im Vergleich zu Atom unglaublich schnell war. Und er unterstützt in seiner Geschwindigkeit eine Vielzahl unterschiedlicher Möglichkeiten.

Wenn Sie auch VS-Code verwenden, empfehle ich die Installation der Project Snippets-Erweiterung . Mit dieser Erweiterung kann der Programmierer benutzerdefinierte Snippets für jeden in einem Projekt verwendeten Arbeitsbereich erstellen. Diese Erweiterung funktioniert genauso wie der in VS Code integrierte Mechanismus zum Verwenden von Snippets. Der Unterschied besteht darin, dass bei der Arbeit mit Projekt- .vscode/snippets/ Ordner .vscode/snippets/ im Projekt erstellt wird. Es sieht aus wie in der folgenden Abbildung.


Der Inhalt des Ordners .vscode / snippets /

6. Erstellen Sie Unit-, End-to-End- und Integrationstests


Mit zunehmender Größe der Anwendung wird es für den Programmierer erschreckender, Code zu bearbeiten, der nicht durch Tests abgedeckt wird. Beispielsweise kann es vorkommen, dass jemand den in src/x/y/z/ gespeicherten Code bearbeitet und beschlossen hat, ihn an die Produktion zu senden. Wenn sich die vorgenommenen Änderungen gleichzeitig auf die Teile des Projekts auswirken, an die der Programmierer nicht gedacht hat, kann alles zu einem Fehler führen, auf den ein echter Benutzer stößt. Wenn das Projekt Tests enthält, kennt der Programmierer den Fehler lange bevor der Code in Produktion geht.

7. Brainstorming


Programmierer, die gerade neue Funktionen in Projekte einführen, lehnen häufig ein Brainstorming ab. Dies geschieht, weil eine solche Aktivität nicht mit dem Schreiben von Code zusammenhängt. Dies ist besonders häufig der Fall, wenn nur sehr wenig Zeit für die Aufgabe zur Verfügung steht.

Und warum müssen Sie übrigens bei der Entwicklung von Anwendungen ein Brainstorming durchführen?

Tatsache ist, dass je komplexer die Anwendung wird, desto mehr Aufmerksamkeit müssen Programmierer ihren einzelnen Teilen widmen. Durch Brainstorming wird die Zeit für die Umgestaltung des Codes verkürzt. Nachdem sie festgehalten wurden, ist der Programmierer mit dem Wissen ausgestattet, was während des Abschlusses des Projekts schief gehen kann. Oft machen sich Programmierer bei der Entwicklung einer Anwendung nicht einmal die Mühe, zumindest ein wenig darüber nachzudenken, wie sie alles optimal machen können.

Deshalb ist Brainstorming sehr wichtig. Während eines solchen Ereignisses kann der Programmierer die Architektur des Codes berücksichtigen, darüber nachdenken, wie die erforderlichen Änderungen am Programm vorgenommen werden können, den Lebenszyklus dieser Änderungen verfolgen und eine Strategie für die Arbeit mit ihnen erstellen. Es lohnt sich nicht, es sich zur Gewohnheit zu machen, alle Pläne ausschließlich im eigenen Kopf zu behalten. Dies ist, was Programmierer tun, die übermäßig zuversichtlich sind. Aber sich an absolut alles zu erinnern ist einfach unmöglich. Und sobald etwas falsch gemacht wird, treten nacheinander Probleme auf. Dies ist das Prinzip der Dominosteine ​​in Aktion.

Brainstorming ist auch in Teams nützlich. Wenn beispielsweise jemand im Laufe der Arbeit auf ein Problem stößt, kann er sich den Materialien der Brainstorming-Sitzung zuwenden, da das bei ihm aufgetretene Problem möglicherweise bereits in Betracht gezogen wurde. Die Notizen, die während der Brainstorming-Sitzung gemacht werden, können durchaus die Rolle eines Plans zur Lösung des Problems spielen. Mit diesem Plan können Sie den Arbeitsaufwand klar einschätzen.

8. Erstellen Sie Anwendungsmodelle


Wenn Sie mit der Entwicklung der Anwendung beginnen möchten, müssen Sie entscheiden, wie sie aussehen soll und wie Benutzer mit ihr interagieren. Dies bedeutet, dass Sie ein Anwendungslayout erstellen müssen. Hierfür können Sie verschiedene Tools verwenden.

Moqups ist eines der App-Mockup- Tools, von denen ich oft höre. Dies ist ein schnelles Tool, das mit HTML5 und JavaScript erstellt wurde und keine besonderen Anforderungen an das System stellt.

Das Erstellen einer Scheinanwendung vereinfacht und beschleunigt den Entwicklungsprozess erheblich. Das Layout gibt dem Entwickler Informationen über die Beziehung zwischen den einzelnen Teilen der Anwendung und darüber, welche Art von Daten auf ihren Seiten angezeigt werden.

9. Planen Sie den Datenfluss in Anwendungen


Fast jede Komponente Ihrer Anwendung wird mit einigen Daten verknüpft. Einige Komponenten verwenden ihre eigenen Datenquellen, aber die meisten Komponenten empfangen Daten von Entitäten über ihnen in der Komponentenhierarchie. Für diejenigen Teile der Anwendung, in denen dieselben Daten von mehreren Komponenten gemeinsam genutzt werden, ist es hilfreich, einen zentralen Informationsspeicher auf der obersten Ebene der Hierarchie bereitzustellen. In solchen Situationen kann die Redux- Bibliothek dem Entwickler wertvolle Unterstützung bieten.

Ich empfehle, während der Arbeit an der Anwendung ein Diagramm zu erstellen, das zeigt, wie sich die Daten in dieser Anwendung bewegen. Dies wird bei der Erstellung eines klaren Anwendungsmodells hilfreich sein. Darüber hinaus sprechen wir über den Code und die Wahrnehmung der Anwendung durch den Programmierer. Ein solches Modell wird außerdem bei der Schaffung von Reduzierstücken helfen.

10. Verwenden Sie Datenzugriffsfunktionen


Mit zunehmender Größe der Anwendung wächst auch die Anzahl ihrer Komponenten. Und wenn die Anzahl der Komponenten zunimmt, geschieht dasselbe mit der Häufigkeit der Verwendung von Selektoren (react-redux ^ v7.1) oder mapStateToProps . Angenommen, Ihre Komponenten oder Hooks greifen häufig mit einem Konstrukt wie useSelector((state) => state.app.user.profile.demographics.languages.main) in verschiedenen Teilen der Anwendung zu. Wenn ja, bedeutet dies, dass Sie über das Erstellen von Datenzugriffsfunktionen nachdenken müssen. Dateien mit solchen Funktionen sollten an einem öffentlichen Ort gespeichert werden, von dem Komponenten und Hooks sie importieren können. Ähnliche Funktionen können Filter, Parser oder andere Funktionen für die Datentransformation sein.

Hier sind einige Beispiele.

Beispielsweise können src/accessors den folgenden Code enthalten:

 export const getMainLanguages = (state) => state.app.user.profile.demographics.languages.main 

Hier ist die Version mit connect , die sich im src/components/ViewUserLanguages :

 import React from 'react' import { connect } from 'react-redux' import { getMainLanguages } from '../accessors' const ViewUserLanguages = ({ mainLanguages }) => ( <div>   <h1>Good Morning.</h1>   <small>Here are your main languages:</small>   <hr />   {mainLanguages.map((lang) => (     <div>{lang}</div>   ))} </div> ) export default connect((state) => ({ mainLanguages: getMainLanguages(state), }))(ViewUserLanguages) 

Hier ist die Version, die useSelector sich unter src/components/ViewUserLanguages :

 import React from 'react' import { useSelector } from 'react-redux' import { getMainLanguages } from '../accessors' const ViewUserLanguages = ({ mainLanguages }) => { const mainLanguages = useSelector(getMainLanguages) return (   <div>     <h1>Good Morning.</h1>     <small>Here are your main languages:</small>     <hr />     {mainLanguages.map((lang) => (       <div>{lang}</div>     ))}   </div> ) } export default ViewUserLanguages 

Bemühen Sie sich außerdem, sicherzustellen, dass solche Funktionen unveränderlich und ohne Nebenwirkungen sind. Finden Sie hier heraus, warum ich eine solche Empfehlung gebe.

11. Steuern Sie den Datenfluss in Eigenschaften mithilfe der Destrukturierungs- und Spread-Syntax


Was sind die Vorteile der Verwendung des Konstrukts props.something gegenüber dem Konstrukt props.something ?

So sieht es aus, ohne Destrukturierung zu verwenden:

 const Display = (props) => <div>{props.something}</div> 

Hier ist das gleiche, aber mit dem Einsatz von Destrukturierung:

 const Display = ({ something }) => <div>{something}</div> 

Die Verwendung der Destrukturierung verbessert die Lesbarkeit des Codes. Dies schränkt seine positiven Auswirkungen auf das Projekt jedoch nicht ein. Durch die Destrukturierung muss der Programmierer entscheiden, was genau die Komponente empfängt und was genau sie ausgibt. Dies erspart jedem, der den Code eines anderen bearbeiten muss, jede Zeile der render auf der Suche nach allen von der Komponente verwendeten Eigenschaften zu durchsuchen.

Darüber hinaus bietet dieser Ansatz eine nützliche Möglichkeit, Standardeigenschaftswerte festzulegen. Dies erfolgt ganz am Anfang des Komponentencodes und macht das Schreiben von zusätzlichem Code in den Komponentenkörper überflüssig:

 const Display = ({ something = 'apple' }) => <div>{something}</div> 

Möglicherweise haben Sie schon einmal das folgende Beispiel gesehen:

 const Display = (props) => ( <Agenda {...props}>   {' '}   //     Agenda   <h2><font color="#3AC1EF">Today is {props.date}</font></h2>   <hr />   <div>     <h3><font color="#3AC1EF">▍Here your list of todos:</font></h3>     {props.children}   </div> </Agenda> ) 

Solche Konstruktionen sind nicht leicht zu lesen, aber dies ist nicht ihr einziges Problem. Es liegt also ein Fehler vor. Wenn die Anwendung auch props.children Komponenten anzeigt, wird props.children zweimal auf dem Bildschirm angezeigt. Wenn die Arbeit an dem Projekt in einem Team durchgeführt wird und die Teammitglieder nicht vorsichtig genug sind, ist die Wahrscheinlichkeit solcher Fehler ziemlich hoch.

Wenn Sie stattdessen Eigenschaften zerstören, wird der Komponentencode klarer und die Fehlerwahrscheinlichkeit verringert sich:

 const Display = ({ children, date, ...props }) => ( <Agenda {...props}>   {' '}   //     Agenda   <h2><font color="#3AC1EF">Today is {date}</font></h2>   <hr />   <div>     <h3><font color="#3AC1EF">▍Here your list of todos:</font></h3>     {children}   </div> </Agenda> ) 

Zusammenfassung


In diesem Artikel haben wir 12 Empfehlungen für diejenigen überprüft, die React-Anwendungen mit Redux entwickeln. Wir hoffen, dass Sie hier etwas finden, das Ihnen nützlich ist.

Liebe Leser! Welche Tipps würden Sie zu den in diesem Artikel hinzugefügten hinzufügen?



Source: https://habr.com/ru/post/de456336/


All Articles