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 beschäftigt. Das Ziel dieses Teams ist es, in die Realität umzusetzen und Ideen zu erkunden, die den Benutzern gefallen könnten. Dies bedeutet, dass Entwickler schnell handeln müssen und häufig Änderungen an den Innovationen vornehmen müssen, die sie erforschen, um die Benutzererfahrung so bequem und angenehm wie möglich zu gestalten. Sie bevorzugen es, 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 Entwickler von Hike anfangen, an etwas Neuem zu arbeiten, haben sie bei der Diskussion der Architektur der untersuchten Lösung mehrere Fragen:
- Dies ist eine experimentelle Gelegenheit, die, wie sie sagen, „nicht hochfliegen“ kann und aufgegeben werden muss. Ist es vor diesem Hintergrund erforderlich, Zeit mit dem Entwerfen der Anwendungsarchitektur zu verbringen?
- Eine experimentelle Anwendung ist nur ein MVP, ein minimal lebensfähiges Produkt mit 1-2 Bildschirmen, das so schnell wie möglich erstellt werden muss. Sollte ich mich vor diesem Hintergrund an Redux wenden?
- Wie rechtfertigen Sie die Zeit, die Produktmanager benötigen, um die unterstützende Infrastruktur für eine experimentelle Anwendung vorzubereiten?
Mit Redux finden Sie die richtigen Antworten auf all diese Fragen. Die Redux-Architektur hilft dabei, den Anwendungsstatus von React zu trennen. Sie können ein globales Repository erstellen, das sich auf der obersten Ebene der Anwendung befindet und Statuszugriff für alle anderen Komponenten bietet.
Aufgabentrennung
Was ist eine „Aufgabentrennung“? Dazu sagt
Wikipedia : „In der Informatik ist die Aufteilung der Verantwortlichkeiten der Prozess der Aufteilung eines Computerprogramms in Funktionsblöcke, die sich so wenig wie möglich gegenseitig überlappen. In einem allgemeineren Fall ist die Aufteilung der Verantwortlichkeiten die Vereinfachung eines einzelnen Prozesses zur Lösung eines Problems, indem er in interagierende Prozesse zur Lösung von Teilaufgaben unterteilt wird.
Mit der Architektur von Redux können Sie das Prinzip der Aufgabentrennung in Anwendungen implementieren und diese in vier Blöcke aufteilen (siehe folgende Abbildung).
Redux-ArchitekturHier ist eine kurze Beschreibung dieser Blöcke:
- Darstellungen oder Benutzeroberflächenkomponenten (UI-Komponenten) ähneln reinen Funktionen (dh Funktionen, die die an sie übertragenen Daten nicht ändern und einige andere Eigenschaften aufweisen), die für die Anzeige von Informationen auf dem Bildschirm auf der Grundlage der vom Geschäft an sie übertragenen Daten verantwortlich sind. Sie ändern Daten nicht direkt. Wenn ein Ereignis eintritt oder der Benutzer mit ihnen interagiert, wenden sie sich an die Ersteller der Aktionen.
- Aktionsersteller sind für das Erstellen und Versenden von Aktionen verantwortlich.
- Reduzierer erhalten geplante Aktionen und aktualisieren den Status des Repositorys.
- Der Datenspeicher ist für die Speicherung der Anwendungsdaten verantwortlich.
Betrachten Sie die Architektur von Redux als Beispiel.
Was ist, wenn verschiedene Komponenten dieselben Daten benötigen?
Die Hike-App verfügt über einen Bildschirm, auf dem die Freundesliste des Benutzers angezeigt wird. Informationen zu ihrer Menge werden oben auf diesem Bildschirm angezeigt.
Freunde-Bildschirm in der Hike-AppHier gibt es 3 Reaktionskomponenten:
FriendRow
ist eine Komponente, die den Freundesnamen des Benutzers und einige andere Informationen über ihn enthält.FriendsHeader
- eine Komponente, die die Aufschrift "MY FRIENDS" und Informationen zur Anzahl der Freunde anzeigt.ContainerView
ist eine Containerkomponente, die den Bildschirmtitel der FriendsHeader
Komponente und die Liste der Freunde kombiniert, die durch Durchlaufen eines Arrays mit Informationen zu den Freunden des Benutzers erhalten werden. Jedes Element wird von der FriendRow
Komponente auf dem Bildschirm angezeigt.
Hier ist der Code für
friendsContainer.js , um
Folgendes zu veranschaulichen:
class Container extends React.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 völlig offensichtliche Möglichkeit, eine solche Anwendungsseite zu erstellen, besteht darin, Daten über Freunde in eine Containerkomponente zu laden und diese als Eigenschaften an untergeordnete Komponenten zu übergeben.
Denken wir an die Tatsache, dass diese Daten über Freunde möglicherweise in einigen anderen in der Anwendung verwendeten Komponenten benötigt werden.
Chat-Bildschirm wandernAngenommen, die Anwendung verfügt über einen Chat-Bildschirm, der auch eine Liste von Freunden enthält. Es ist ersichtlich, dass dieselben Daten auf dem Bildschirm mit der Liste der Freunde und auf dem Chat-Bildschirm verwendet werden. Was tun in einer ähnlichen Situation? Wir haben zwei Möglichkeiten:
- Sie können Ihre Freundesdaten erneut in der
ComposeChat
Komponente ComposeChat
, die für die Anzeige von Chat-Listen verantwortlich ist. Dieser Ansatz ist jedoch nicht besonders gut, da seine Verwendung eine Duplizierung von Daten bedeutet und zu Problemen bei der Synchronisation führen kann. - Sie können Daten über Freunde in einer Komponente der obersten Ebene (dem Hauptcontainer der Anwendung) herunterladen und diese Daten an die Komponenten übertragen, die für die Anzeige einer Liste von Freunden und die Auflistung von Chatrooms verantwortlich sind. Darüber hinaus müssen wir Funktionen an diese Komponenten übergeben, um die Daten der Freunde zu aktualisieren. Dies ist erforderlich, um die Datensynchronisation zwischen den 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 verwenden
Hier geht es darum, die Arbeit mit Daten mithilfe von Speicher, Aktionserstellern, Reduzierern und zwei Komponenten der Benutzeroberfläche zu organisieren.
▍1. Data Warehouse
Das Repository enthält hochgeladene Daten über die Freunde des Benutzers. Diese Daten können an jede Komponente gesendet werden, falls sie dort benötigt werden.
▍2. Aktionsersteller
In diesem Fall wird der Ersteller der Aktion zum Versenden von Ereignissen verwendet, die darauf abzielen, Daten über Freunde zu speichern und zu aktualisieren. Hier ist der Code für
friendsActions.js :
export const onFriendsFetch = (friendsData) => { return { type: 'FRIENDS_FETCHED', payload: friendsData }; };
▍3. Reduzierstücke
Reduzierer warten auf Ereignisse, die geplante Aktionen darstellen, und aktualisieren ihre Freunde. Hier ist der Code für
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. Freundesliste Komponente
Diese Containerkomponente zeigt die Daten von Freunden an und aktualisiert die Benutzeroberfläche, wenn sie sich ändern. Darüber hinaus ist er für das Herunterladen von Daten aus dem Repository verantwortlich, wenn er diese nicht hat. Hier ist der Code für
friendsContainer.js :
class Container extends React.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)); } }); export default connect(mapStateToProps, mapActionToProps)(Container);
▍5. Chat-Listing-Komponente
Diese Containerkomponente verwendet auch Daten aus dem Speicher und reagiert auf deren Aktualisierung.
Informationen zur Implementierung der Redux-Architektur
Es kann ein oder zwei Tage dauern, bis die oben beschriebene Architektur funktionsfähig ist. Wenn jedoch Änderungen am Projekt vorgenommen werden müssen, werden diese sehr einfach und schnell vorgenommen. Wenn Sie der Anwendung eine neue Komponente hinzufügen müssen, die Daten über Freunde verwendet, können Sie dies tun, ohne sich um die Datensynchronisierung kümmern zu müssen oder andere Komponenten wiederholen 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 Benutzeroberflächenkomponente leicht einem Komponententest unterzogen werden, da 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, die während der Datenvisualisierung auftreten.
Jede Komponente kann anhand verschiedener Daten umfassend getestet werden. Solche Tests decken versteckte Probleme auf und tragen dazu bei, einen qualitativ hochwertigen Code sicherzustellen.
Es ist zu beachten, dass nicht nur die für die Datenvisualisierung verantwortlichen Komponenten, 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 mit Redux
▍ Überschüssiger Vorlagencode
Um die Redux-Architektur in einer Anwendung zu implementieren, müssen Sie viel Zeit aufwenden und auf alle möglichen seltsamen Konzepte und Entitäten stoßen.
Dies sind die sogenannten Schlitten (Thunks), Reduzierer (Reduzierer), Aktionen (Aktionen), Middleware-Schichten (Middlewares), dies sind die Funktionen
mapStateToProps
und
mapDispatchToProps
sowie vieles mehr. Es braucht Zeit, um all dies zu lernen, und um zu lernen, wie man es richtig benutzt, ist Übung erforderlich. Das Projekt enthält viele Dateien. Beispielsweise kann es aufgrund einer geringfügigen Änderung der Komponente für die Datenvisualisierung erforderlich sein, Änderungen an vier Dateien vorzunehmen.
Red Redux Vault ist Singleton
In Redux wird das Data Warehouse nach 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 Komponente gibt. Wenn sich Daten in einer dieser Instanzen ändern, wirken sich diese Änderungen auf eine andere Instanz aus. In bestimmten Fällen ist dieses Verhalten möglicherweise nicht wünschenswert, und es kann erforderlich sein, dass jede Instanz der Komponente eine eigene Kopie der Daten verwendet.
Zusammenfassung
Erinnern Sie sich an unsere Hauptfrage, ob es sich lohnt, die Redux-Architektur zu implementieren. Als Antwort auf diese Frage sagen wir Redux "Ja". Diese Architektur spart Zeit und Mühe bei der Entwicklung und Entwicklung von Anwendungen. Die Verwendung von Redux erleichtert Programmierern das häufige Ändern der Anwendung und erleichtert das Testen. Natürlich bietet die Redux-Architektur eine beträchtliche Menge an Boilerplate-Code, aber sie hilft dabei, den Code in Module zu unterteilen, mit denen bequem gearbeitet werden kann. Jedes dieser Module kann unabhängig von den anderen getestet werden. Dies hilft, Fehler in der Entwicklungsphase zu identifizieren und qualitativ hochwertige Programme sicherzustellen.
Liebe Leser! Verwenden Sie Redux in Ihren Projekten?
