Redux-Architektur. Ja oder Nein?

Ursprünglicher Autor: Ankit Goyal
  • Übersetzung
Der Autor des Materials, dessen Übersetzung wir heute veröffentlichen, sagt, dass er Teil des Hike- Messenger-Teams ist , das sich mit neuen Funktionen der Anwendung befasst. Das Ziel dieses Teams ist es, in die Realität umzusetzen und Ideen zu erforschen, die den Benutzern gefallen könnten. Dies bedeutet, dass Entwickler schnell handeln müssen und häufig Änderungen an den von ihnen untersuchten Innovationen vornehmen müssen, um die Arbeit der Benutzer so komfortabel und angenehm wie möglich zu gestalten. Sie ziehen es vor, ihre Experimente mit React Native durchzuführen, da diese Bibliothek die Entwicklung beschleunigt und es Ihnen ermöglicht, denselben Code auf verschiedenen Plattformen zu verwenden. Außerdem verwenden sie die Redux-Bibliothek.



Wenn die Entwickler von Hike anfangen, an etwas Neuem zu arbeiten, haben sie bei der Diskussion über die Architektur der untersuchten Lösung mehrere Fragen:

  • Dies ist eine experimentelle Möglichkeit, die, wie sie sagen, „nicht abheben“ kann und aufgegeben werden muss. Ist es angesichts dessen erforderlich, Zeit mit dem Entwerfen einer Anwendungsarchitektur zu verbringen?
  • Die experimentelle Anwendung ist nur MVP, ein Produkt mit minimaler Lebensdauer, das über 1-2 Bildschirme verfügt und so schnell wie möglich erstellt werden muss. Lohnt es sich angesichts dessen, Redux zu kontaktieren?
  • Wie kann man den Produktmanagern den Zeitaufwand für die Vorbereitung der Hilfsinfrastruktur der Versuchsanwendung rechtfertigen?

Tatsächlich hilft Redux, die richtigen Antworten auf all diese Fragen zu finden. Die Redux-Architektur hilft, den Status der Anwendung von React zu trennen. Sie können einen globalen Speicher auf der obersten Ebene der Anwendung erstellen und den Statuszugriff für alle anderen Komponenten bereitstellen.

Aufgabentrennung


Was ist eine „Aufgabenteilung“? Dazu schreibt Wikipedia : „In der Informatik besteht die Aufteilung der Zuständigkeiten darin, ein Computerprogramm in Funktionsblöcke zu unterteilen, die die Funktionen des anderen so wenig wie möglich überlappen. In einem allgemeineren Fall ist die Aufteilung der Zuständigkeiten die Vereinfachung eines einzelnen Prozesses zur Lösung eines Problems durch Aufteilung in interaktive Prozesse zur Lösung von Teilaufgaben. “

Mit der Redux-Architektur können Sie das Prinzip der Aufteilung von Verantwortlichkeiten in Anwendungen implementieren, indem Sie diese in vier Blöcke aufteilen (siehe folgende Abbildung).


Redux-Architektur

Hier eine kurze Beschreibung dieser Blöcke:

  • Ansichten oder Benutzeroberflächenkomponenten (UI-Komponenten) ähneln reinen Funktionen (dh Funktionen, die die an sie übergebenen Daten nicht ändern und einige andere Eigenschaften aufweisen), die für die Anzeige von Informationen auf dem Bildschirm verantwortlich sind, die auf den vom Repository an sie übergebenen Daten basieren. Sie ändern die Daten nicht direkt. Wenn ein Ereignis eintritt oder ein Benutzer mit ihnen interagiert, wenden Sie sich an den Ersteller der Aktion.
  • Aktionsersteller sind für das Erstellen und Versenden von Aktionen verantwortlich.
  • Reduzierungen empfangen ausgelöste Aktionen und aktualisieren den Status des Speichers.
  • Der Datenspeicher ist für die Speicherung der Anwendungsdaten verantwortlich.

Betrachten Sie die Redux-Architektur anhand eines Beispiels.

Was ist, wenn verschiedene Komponenten dieselben Daten benötigen?


Die Hike-App verfügt über einen Bildschirm, auf dem eine Liste der Freunde des Benutzers angezeigt wird. Im oberen Teil dieses Bildschirms werden Informationen zu ihrer Anzahl angezeigt.


Ein Bildschirm mit Informationen zu Freunden in der Hike-Anwendung.

Hier gibt es 3 React-Komponenten:

  • FriendRow - eine Komponente, die den Namen des Freundes des Benutzers und einige andere Informationen über ihn enthält.
  • FriendsHeader - Eine Komponente, die die Aufschrift "MEINE FREUNDE" und Informationen über die Anzahl der Freunde anzeigt.
  • ContainerView- eine Containerkomponente, die den Bildschirmheader, der von der Komponente dargestellt wird, FriendsHeaderund die Liste der Freunde kombiniert, die durch Durchlaufen eines Arrays mit Informationen über die Freunde des Benutzers erhalten wird, wobei jedes Element eine auf dem Bildschirm dargestellte Komponente ist FriendRow.

Hier ist der Code für die Datei friendsContainer.js , in der Folgendes dargestellt ist:

classContainerextendsReact.Component{
    constructor(props) {
      super(props);
      this.state = {
        friends: []
      };
    }
    componentDidMount() {
      FriendsService.fetchFriends().then((data) => {
        this.setState({
          friends: data
        });
      });
    }
    render() {
      const { friends } = this.state;
      return (
        <View style={styles.flex}>
        <FriendsHeader count={friends.length} text='My friends' />
        {friends.map((friend) => (<FriendRow {...friend} />)) }
        </View>
      );
    }
}

Eine absolut naheliegende Möglichkeit, eine solche Anwendungsseite zu erstellen, besteht darin, Daten über Freunde in einen Komponentencontainer zu laden und diese als Eigenschaften an untergeordnete Komponenten zu übergeben.

Nehmen wir jetzt an, dass diese Daten über Freunde in einigen anderen in der Anwendung verwendeten Komponenten benötigt werden.


Chat-Bildschirm in einer Hike-Anwendung

Angenommen, die Anwendung verfügt über einen Chat-Bildschirm, der auch eine Liste von Freunden enthält. Es ist zu sehen, dass auf dem Bildschirm mit der Freundesliste und auf dem Chat-Bildschirm dieselben Daten verwendet werden. Was ist in dieser Situation zu tun? Wir haben zwei Möglichkeiten:

  • Sie können die Daten der Freunde erneut in die Komponente laden, ComposeChatdie für die Anzeige der Chatlisten verantwortlich ist. Dieser Ansatz ist jedoch nicht besonders gut, da seine Verwendung zu doppelten Daten führt und zu Problemen bei der Synchronisierung führen kann.
  • Sie können Daten über Freunde in die übergeordnete Komponente (den Hauptcontainer der Anwendung) laden und diese Daten an die Komponenten übertragen, die für die Anzeige der Freundesliste und die Auflistung der Chats verantwortlich sind. Außerdem müssen Funktionen auf diese Komponenten übertragen werden, um die Freundesdaten zu aktualisieren. Dies ist erforderlich, um die Datensynchronisierung zwischen Komponenten zu unterstützen. Dieser Ansatz führt dazu, dass die Komponente der obersten Ebene buchstäblich mit Methoden und Daten gepackt wird, die sie nicht direkt verwendet.

Beide Optionen sind nicht so attraktiv. Schauen wir uns nun an, wie unser Problem mithilfe der Redux-Architektur gelöst werden kann.

Redux benutzen


Hier geht es um die Organisation der Arbeit mit Daten mithilfe des Repository, Aktionserstellern, Reduzierern und zwei Komponenten der Benutzeroberfläche.

▍1. Datenspeicherung


Das Repository enthält heruntergeladene Daten zu den Freunden des Benutzers. Diese Daten können an jede Komponente gesendet werden, wenn sie dort benötigt werden.

▍2. Action-Schöpfer


In diesem Fall wird der Ersteller der Aktion zum Versenden von Ereignissen verwendet, um Daten zu Freunden zu speichern und zu aktualisieren. Hier ist der Code für die Datei friendsActions.js :

exportconst onFriendsFetch = (friendsData) => {
  return {
    type: 'FRIENDS_FETCHED',
    payload: friendsData
  };
};

▍3. Reduktoren


Reduzierer warten auf das Eintreffen von Ereignissen, die Dispatching-Aktionen darstellen, und aktualisieren die Daten über Freunde. Hier ist der Code für die Datei friendsReducer.js :

const INITIAL_STATE = {
       friends: [],
    friendsFetched: false
};
function(state = INITIAL_STATE, action){
    switch(action.type) {
    case'FRIENDS_FETCHED':
        return {
            ...state,
            friends: action.payload,
            friendsFetched: true
        };
    }
}

▍4. Freundelistenkomponente


Diese Containerkomponente zeigt Freunde an und aktualisiert die Benutzeroberfläche, sobald sie sich ändern. Außerdem ist er dafür verantwortlich, Daten aus dem Speicher herunterzuladen, falls er sie nicht hat. Hier ist der Code für die Datei friendsContainer.js :

classContainerextendsReact.Component {
    constructor(props) {
      super(props);
    }
    componentDidMount() {
      if(!this.props.friendsFetched) {
        FriendsService.fetchFriends().then((data) => {
          this.props.onFriendsFetch(data);
        });
      }
    }
    render() {
      const { friends } = this.props;
      return (
        <View style={styles.flex}>
        <FriendsHeader count={friends.length} text='My friends' />
        {friends.map((friend) => (<FriendRow {...friend} />)) }
        </View>
      );
    }
}
const mapStateToProps = (state) => ({
  ...state.friendsReducer
});
const mapActionToProps = (dispatch) => ({
  onFriendsFetch: (data) => {
    dispatch(FriendActions.onFriendsFetch(data)); 
  }
});
exportdefault connect(mapStateToProps, mapActionToProps)(Container);

▍5. Komponente, die die Chatliste anzeigt


Diese Containerkomponente verwendet auch Daten aus dem Repository und reagiert auf deren Aktualisierung.

Über die Implementierung der Redux-Architektur


Um die obige Architektur in einen funktionsfähigen Zustand zu versetzen, kann es ein oder zwei Tage dauern, aber wenn Änderungen am Projekt vorgenommen werden müssen, werden sie sehr einfach und schnell durchgeführt. Wenn Sie Ihrer Anwendung eine neue Komponente hinzufügen müssen, die Freundesdaten verwendet, können Sie dies tun, ohne sich um die Datensynchronisierung oder das Wiederherstellen anderer Komponenten sorgen zu müssen. Gleiches gilt für das Entfernen von Bauteilen.

Testen


Bei Verwendung von Redux kann jeder Anwendungsblock unabhängig getestet werden.
Beispielsweise kann jede Komponente der Benutzerschnittstelle leicht einem Komponententest unterzogen werden, da sich herausstellt, dass sie datenunabhängig ist. Der Punkt ist, dass eine Funktion, die eine solche Komponente darstellt, immer dieselbe Darstellung für dieselben Daten zurückgibt. Dies macht die Anwendung vorhersehbar und verringert die Wahrscheinlichkeit von Fehlern bei der Datenvisualisierung.

Jede Komponente kann anhand einer Vielzahl von Daten umfassend getestet werden. Solche Tests ermöglichen es, versteckte Probleme aufzudecken und tragen dazu bei, die hohe Qualität des Codes sicherzustellen.

Es ist zu beachten, dass nicht nur Komponenten, die für die Datenvisualisierung verantwortlich sind, sondern auch Reduzierer und Aktionsersteller unabhängigen Tests unterzogen werden können.

Redux ist großartig, aber mit dieser Technologie sind wir auf einige Schwierigkeiten gestoßen.

Schwierigkeiten bei der Verwendung von Redux


▍ Überschüssiger Vorlagencode


Um die Redux-Architektur in einer Anwendung zu implementieren, müssen Sie viel Zeit damit verbringen, auf alle möglichen seltsamen Konzepte und Entitäten zu stoßen.

Diese so genannte Schlitten (Thunks), redyusery (Reduzierungen), Aktionen (Aktionen), die Zwischensoftwareschichten (Middle), diese Funktion mapStateToPropsund mapDispatchToPropssowie viele andere Dinge. Es braucht Zeit, um dies alles zu lernen, aber es braucht Übung, um zu lernen, wie man es richtig benutzt. In einem Projekt befinden sich viele Dateien, und beispielsweise kann eine geringfügige Änderung an einer Komponente zur Visualisierung von Daten dazu führen, dass Änderungen an vier Dateien vorgenommen werden müssen.

Red Redux Vault ist ein Singleton


In Redux wird das Data Warehouse mit dem Singleton-Muster erstellt, obwohl Komponenten mehrere Instanzen haben können. Meistens ist dies kein Problem, aber in bestimmten Situationen kann ein solcher Ansatz zur Datenspeicherung einige Schwierigkeiten verursachen. Stellen Sie sich zum Beispiel vor, dass es zwei Instanzen einer bestimmten Komponente gibt. Wenn sich Daten in einer dieser Instanzen ändern, wirken sich die Änderungen auch auf die andere Instanz aus. In bestimmten Fällen kann dieses Verhalten unerwünscht sein und es kann erforderlich sein, dass jede Instanz der Komponente eine eigene Kopie der Daten verwendet.

Ergebnisse


Erinnern Sie sich an unsere Hauptfrage, ob Zeit und Mühe für die Implementierung der Redux-Architektur aufgewendet werden müssen. Als Antwort auf diese Frage sagen wir Redux "Ja". Diese Architektur spart Zeit und Mühe beim Entwickeln und Entwickeln von Anwendungen. Die Verwendung von Redux erleichtert Programmierern das häufige Ändern der Anwendung und das Testen. Natürlich bietet die Redux-Architektur eine beträchtliche Menge an Vorlagencode, aber es trägt dazu bei, den Code in Module aufzuteilen, mit denen man bequem arbeiten kann. Jedes dieser Module kann unabhängig von den anderen getestet werden, was zur Identifizierung von Fehlern in der Entwurfsphase beiträgt und qualitativ hochwertige Programme ermöglicht.

Sehr geehrte Leser! Verwenden Sie Redux in Ihren Projekten?


Jetzt auch beliebt: