Ersetzen Haken in React Redux?

Seit Hooks in React erschienen sind, gab es viele Fragen, ob sie Redux ersetzen können.

Ich glaube, dass Hooks und Redux wenig gemeinsam haben. Haken geben uns keine neuen erstaunlichen Möglichkeiten, mit dem Staat zusammenzuarbeiten. Stattdessen erweitern sie die APIs, damit sie in React das tun können, was darin bereits möglich war. Die Hook-API hat die Arbeit mit den Statusverwaltungsfunktionen von React jedoch wesentlich komfortabler gemacht. Es stellte sich heraus, dass es einfacher ist, die neuen Funktionen für die Arbeit mit dem Status zu verwenden als die alten, die in den auf Klassen basierenden Komponenten verfügbar waren. Jetzt benutze ich die Werkzeuge viel häufiger als zuvor, um mit dem Zustand von Komponenten zu arbeiten. Natürlich mache ich das nur, wenn es angebracht ist.



Um meine Einstellung zu React Hooks und Redux zu erläutern, möchte ich zunächst auf die Situationen eingehen, in denen Redux normalerweise verwendet wird.

Was ist Redux?


Redux ist eine Bibliothek, die vorhersagbaren Anwendungsstatus-Speicher implementiert. Es ist auch eine Architektur, die sich nahtlos in React integriert.

Hier sind die Hauptstärken von Redux:

  • Deterministische Darstellung des Zustands (in Kombination mit reinen Komponenten können deterministische visuelle Elemente gebildet werden).
  • Unterstützung für Änderungen des Transaktionsstatus.
  • Isolierung des Zustandsmanagements von E / A-Mechanismen und Nebenwirkungen.
  • Das Vorhandensein einer einzigen Quelle zuverlässiger Daten für den Staat.
  • Einfache Organisation der Zusammenarbeit mit dem Staat in verschiedenen Komponenten.
  • Transaktionsanalysetools (automatische Protokollierung von Aktionsobjekten).
  • Debugging mit der Fähigkeit, den Programmausführungsprozess aufzuzeichnen und abzuspielen (Time Travel Debugging, TTD).

Mit anderen Worten, mit Redux können Sie Ihren Code gut organisieren und bequem debuggen. Redux hilft bei der Entwicklung von Anwendungen, die einfach zu warten sind. Die Verwendung dieser Bibliothek erleichtert das Auffinden der Problemquellen, die in Programmen auftreten.

Was sind React Hooks?


React Hooks ermöglichen es, bei der Arbeit mit Funktionskomponenten ein Analogon des Zustands von Komponenten zu verwenden, das auf Klassen und Analoga ihrer Lebenszyklusmethoden basiert. Hooks erschienen in React 16.8.

Zu den Hauptstärken von Haken gehören:

  • Die Fähigkeit, Status- und Komponentenlebenszyklusereignisse zu verwenden, ohne klassenbasierte Komponenten zu verwenden.
  • Gemeinsame Speicherung verwandter Logik an derselben Komponentenposition, anstatt ähnliche Logik auf mehrere Lebenszyklusmethoden aufzuteilen.
  • Freigabemechanismen unabhängig von der Komponentenimplementierung (dies ähnelt der Render-Prop- Vorlage).

Beachten Sie, dass diese großartigen Funktionen Redux nicht überwältigen. React-Hooks können und sollten verwendet werden, um deterministische Statusaktualisierungen durchzuführen. Dies war jedoch schon immer eine der React-Funktionen, und das deterministische Redux-Statusmodell lässt sich gut mit dieser Funktion kombinieren. Auf diese Weise erreicht React Determinismus bei der Ausgabe visueller Elemente, und dies ist ohne Übertreibung eines der treibenden Motive für die Erstellung von React.

Wenn Sie Tools wie die React -Redux-API mit Hooks oder den React UseReducer-Hook verwenden , gibt es keinen Grund zu fragen, was Sie wählen sollen - Hooks oder Redux. Sie können diese Technologien sowohl kombinieren als auch kombinieren.

Was ersetzen Haken?


Nach dem Aufkommen der Hook-APIs habe ich die folgenden Technologien nicht mehr verwendet:


Was ersetzen keine Haken?


Ich benutze immer noch oft die folgenden Technologien:

  • Redux - aus all den oben genannten Gründen.
  • Komponenten höherer Ordnung - zum Zwecke der Komponentenzusammensetzung in Fällen, in denen ich End-to-End-Funktionen implementieren muss, die von allen oder einigen visuellen Komponenten der Anwendung gemeinsam genutzt werden. Zu diesen Funktionen gehören Redux-Anbieter, Seitenlayout-Systeme, Unterstützungssysteme für Anwendungseinstellungen, Authentifizierungs- und Autorisierungstools, Tools zur Internationalisierung von Anwendungen usw.
  • Trennung zwischen Containerkomponenten und Komponenten, die eine visuelle Darstellung haben. Auf diese Weise können Sie die Modularität und Testbarkeit von Anwendungen verbessern. Es ist besser, Effekte und reine Logik zu trennen.

Wann werden Haken verwendet?


Es ist nicht erforderlich, Redux in jeder Anwendung und in jeder Komponente zu verwenden. Wenn Ihr Projekt aus einer visuellen Komponente besteht, wenn es keine Daten speichert und keine Daten von dort lädt, wenn keine asynchronen E / A-Vorgänge darin ausgeführt werden, kann ich keinen triftigen Grund finden, dieses Projekt durch die Verwendung von Redux zu komplizieren.

Gleiches gilt für Komponenten mit folgenden Merkmalen:

  • Sie verwenden keine Netzwerkressourcen.
  • Sie speichern keine Daten im Status und laden sie nicht von dort.
  • Sie teilen den Status nicht mit anderen Komponenten, die nicht ihre Nachkommen sind.
  • Sie haben keinen eigenen Status, der für die kurzfristige Datenspeicherung verwendet wird.

In bestimmten Situationen haben Sie möglicherweise gute Gründe, das Standardmodell für den Komponentenstatus von React zu verwenden. In solchen Situationen leisten React Hooks gute Arbeit. In dem unten beschriebenen Formular wird beispielsweise der lokale Status der Komponente mithilfe des useState React useState .

 import React, { useState } from 'react'; import t from 'prop-types'; import TextField, { Input } from '@material/react-text-field'; const noop = () => {}; const Holder = ({  itemPrice = 175,  name = '',  email = '',  id = '',  removeHolder = noop,  showRemoveButton = false, }) => {  const [nameInput, setName] = useState(name);  const [emailInput, setEmail] = useState(email); const setter = set => e => {    const { target } = e;    const { value } = target;    set(value);  }; return (    <div className="row">      <div className="holder">        <div className="holder-name">          <TextField label="Name">            <Input value={nameInput} onChange={setter(setName)} required />          </TextField>        </div>        <div className="holder-email">          <TextField label="Email">            <Input              value={emailInput}              onChange={setter(setEmail)}              type="email"              required            />          </TextField>        </div>        {showRemoveButton && (          <button            className="remove-holder"            aria-label="Remove membership"            onClick={e => {              e.preventDefault();              removeHolder(id);            }}          >            ×          </button>        )}      </div>      <div className="line-item-price">${itemPrice}</div>      <style jsx>{cssHere}</style>    </div>  ); }; Holder.propTypes = {  name: t.string,  email: t.string,  itemPrice: t.number,  id: t.string,  removeHolder: t.func,  showRemoveButton: t.bool, }; export default Holder; 

Hier wird useState verwendet, um den kurz verwendeten Status der name und email Eingabefelder zu steuern:

 const [nameInput, setName] = useState(name); const [emailInput, setEmail] = useState(email); 

Möglicherweise stellen Sie fest, dass noch ein Ersteller der Aktion removeHolder in die Eigenschaften von Redux removeHolder . Wie bereits erwähnt, ist die Kombination und Kombination von Technologien völlig normal.

Die Verwendung des lokalen Status einer Komponente zur Lösung solcher Probleme hat immer gut ausgesehen, aber vor React-Hooks hätte ich auf jeden Fall die Komponentendaten im Redux-Speicher speichern und den Status aus den Eigenschaften abrufen wollen.

Zuvor umfasste das Arbeiten mit dem Status einer Komponente die Verwendung klassenbasierter Komponenten, das Schreiben der Anfangsdaten in den Status mithilfe der Mechanismen zum Deklarieren von Klasseneigenschaften (oder im Klassenkonstruktor) usw. Als Ergebnis stellte sich heraus, dass die Komponente zu kompliziert sein musste, um die Verwendung von Redux zu vermeiden. Redux sprach sich auch für die Existenz praktischer Tools zur Verwaltung des Formularstatus mithilfe von Redux aus. Daher hätte ich mir vorher keine Sorgen gemacht, dass der temporäre Status des Formulars am selben Ort wie die Daten mit einer längeren Lebensdauer gespeichert wird.

Da ich Redux bereits in all meinen mehr oder weniger komplexen Anwendungen verwendet habe, hat mich die Wahl der Technologie zum Speichern des Zustands von Komponenten von Komponenten nicht viel nachgedacht. Ich habe in fast allen Fällen nur Redux verwendet.

Unter modernen Bedingungen ist es auch einfach, eine Auswahl zu treffen: Die Arbeit mit dem Status der Komponente wird mithilfe von Standardreaktionsmechanismen organisiert und der Status der Anwendung mithilfe von Redux verwaltet.

Wann sollte Redux verwendet werden?


Eine weitere häufig gestellte Frage zur Statusverwaltung lautet: „Muss ich absolut alles in das Redux-Repository stellen? Wenn ich es nicht tue, wird es die Fähigkeit zum Debuggen von Anwendungen mithilfe von TTD-Mechanismen verletzen? "

Es ist nicht erforderlich, absolut alles im Redux-Repository zu hosten. Tatsache ist, dass Anwendungen viele temporäre Daten verwenden, die zu verstreut sind, um Informationen zu liefern, die dem Entwickler beim Auffinden von Problemen erheblich helfen können, wenn sie im Protokoll aufgezeichnet oder beim Debuggen verwendet werden. Wahrscheinlich müssen Sie, es sei denn, Sie schreiben eine Echtzeit-Editoranwendung, nicht jede Mausbewegung oder jeden Tastendruck in den Status schreiben. Wenn Sie etwas in einen Redux-Status versetzen, fügen Sie der Anwendung eine zusätzliche Abstraktionsebene sowie eine damit verbundene zusätzliche Komplexitätsebene hinzu.

Mit anderen Worten, Sie können Redux sicher verwenden, aber es muss einen Grund dafür geben. Die Verwendung von Redux-Funktionen in Komponenten kann gerechtfertigt sein, wenn sich die Komponenten in den folgenden Funktionen unterscheiden:

  • Sie verwenden I / O. Sie arbeiten beispielsweise mit einem Netzwerk oder mit bestimmten Geräten.
  • Sie speichern Daten darin oder laden Daten daraus.
  • Sie arbeiten mit ihrem Zustand in Verbindung mit Komponenten, die nicht ihre Nachkommen sind.
  • Sie befassen sich mit jeder Geschäftslogik, mit der sich auch andere Teile der Anwendung befassen - sie verarbeiten Daten, die in anderen Teilen der Anwendung verwendet werden.

Hier ist ein weiteres Beispiel aus der TDDDay- Anwendung:

 import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { compose } from 'ramda'; import page from '../../hocs/page.js'; import Purchase from './purchase-component.js'; import { addHolder, removeHolder, getHolders } from './purchase-reducer.js'; const PurchasePage = () => {  //      // mapStateToProps  mapDispatchToProps  const dispatch = useDispatch();  const holders = useSelector(getHolders); const props = {    //           //    dispatch.    addHolder: compose(      dispatch,      addHolder    ),    removeHolder: compose(      dispatch,      removeHolder    ),    holders,  }; return <Purchase {...props} />; }; // `page` -    ,    //        . export default page(PurchasePage); 

Dieses Dokument befasst sich nicht mit dem DOM. Dies ist eine Präsentationskomponente. Es ist über die React -Redux-API mit Hook-Unterstützung mit Redux verbunden.

Redux wird hier verwendet, da die Daten, die in diesem Formular verarbeitet werden, in anderen Teilen der Benutzeroberfläche verwendet werden müssen. Und nachdem der Kaufvorgang abgeschlossen ist, müssen wir die relevanten Informationen in der Datenbank speichern.

Die Statusfragmente, mit denen dieser Code arbeitet, werden von verschiedenen Komponenten verwendet, sie werden nicht nur von einer Komponente verarbeitet. Dies sind keine Daten, die nur für kurze Zeit existieren. Diese Daten können als permanent betrachtet werden. Sie können auf verschiedenen Bildschirmen der Anwendung und in mehreren Sitzungen verwendet werden. All dies sind Szenarien, in denen die Zustände der Komponente zum Speichern von Daten nicht angewendet werden können. Dies ist zwar weiterhin möglich, jedoch nur, wenn der Ersteller der Anwendung basierend auf der React-API eine eigene Bibliothek zum Verwalten des Status schreibt. Dies ist viel schwieriger als nur Redux zu verwenden.

Die React Suspense- API kann in Zukunft nützlich sein, wenn Daten in einem bestimmten Zustand gespeichert und daraus geladen werden. Wir müssen auf die Veröffentlichung warten und prüfen, ob die Vorlagen zum Speichern und Laden von Redux-Daten ersetzt werden können. Mit Redux können wir die Nebenwirkungen klar vom Rest der Komponentenlogik trennen, während wir nicht auf besondere Weise mit E / A-Diensten arbeiten müssen. (Der Grund, warum ich die Redux-Saga- Bibliothek der Redux-Thunk-Middleware vorziehe, ist die Effektisolierung ). Um in diesem Szenario mit Redux konkurrieren zu können, muss die React-API eine Effektisolierung bereitstellen.

Redux ist Architektur


Redux ist viel mehr (und oft viel weniger) als eine State-Management-Bibliothek. Es ist auch eine Teilmenge der Flux- Architektur, die viel strenger definiert, wie Statusänderungen implementiert werden. Lesen Sie hier mehr über die Redux-Architektur.

Ich verwende häufig Reduzierungen, die im Redux-Stil erstellt wurden, wenn ich den komplexen Status der Komponente beibehalten muss, aber die Redux-Bibliothek nicht verwenden muss. Ich verwende auch Aktionen, die im Geiste von Redux erstellt wurden (und sogar Redux-Tools wie Autodux und Redux-Saga ), um Aktionen an Node.js-Anwendungen zu senden. Ich importiere jedoch nicht einmal Redux in solche Anwendungen.

Das Redux-Projekt war schon immer eher eine Architektur und eine Reihe freiwilliger Vereinbarungen als eine Bibliothek. Tatsächlich kann die grundlegende Implementierung von Redux buchstäblich in ein paar Dutzend Codezeilen dargestellt werden.

Dies wird sich als gute Nachricht für diejenigen herausstellen, die den lokalen Status von Komponenten häufiger mit Hooks verwenden und nicht alles an Redux binden möchten.

React unterstützt den useReducer Hook, der mit Redux-Reduzierern arbeiten kann. Dies ist gut für die Implementierung einer nicht trivialen Logik der Arbeit mit dem Staat, für die Arbeit mit abhängigen Fragmenten des Staates und so weiter. Wenn Sie auf ein Problem stoßen, für das der temporäre Status einer einzelnen Komponente geeignet ist, können Sie die Redux-Architektur verwenden, um mit diesem Status zu arbeiten. Anstelle der Redux-Bibliothek können Sie jedoch den useReducer Hook verwenden, um den Status zu verwalten.

Wenn Sie später eine dauerhafte Speicherung von Daten einrichten müssen, die Sie zuvor nur vorübergehend gespeichert haben, sind Sie zu 90% für eine solche Änderung bereit. Sie müssen lediglich die Komponente mit dem Redux-Repository verbinden und dort den entsprechenden Reduzierer hinzufügen.

Fragen und Antworten


Ist der Determinismus gebrochen, wenn Redux nicht alle Anwendungsdaten verwaltet?


Nein, es ist nicht kaputt. Tatsächlich macht die Verwendung von Redux ein Projekt nicht deterministisch. Aber die Vereinbarungen sind. Wenn Sie möchten, dass Ihr Redux-Status deterministisch ist, verwenden Sie reine Funktionen . Gleiches gilt für Situationen, in denen der temporäre Zustand lokaler Komponenten bestimmt werden muss.

▍ Sollte die Redux-Bibliothek die Rolle einer einzigen Quelle zuverlässiger Daten spielen?


Das Prinzip einer einzigen Quelle zuverlässiger Daten bedeutet nicht, dass alle im Anwendungsstatus enthaltenen Daten an einem Ort gespeichert werden müssen. Die Bedeutung dieses Prinzips ist, dass jedes Fragment des Staates nur eine Quelle zuverlässiger Daten haben sollte. Infolgedessen können wir viele Zustandsfragmente haben, von denen jedes seine eigene Quelle zuverlässiger Daten hat.

Dies bedeutet, dass der Programmierer entscheiden kann, was an Redux und was an den Status der Komponenten übertragen wird. Zustandsbestimmende Daten können auch aus anderen Quellen entnommen werden. Zum Beispiel über eine Browser-API, mit der Sie mit Informationen zur Adresse der angezeigten Seite arbeiten können.

Redux ist ein großartiges Tool, um eine einzige Quelle zuverlässiger Daten für den Status einer Anwendung zu unterstützen. Befindet sich der Status der Komponente jedoch ausschließlich in dieser Komponente und wird er verwendet, verfügt dieser Status per Definition bereits über eine einzige Quelle zuverlässiger Daten - den Status der React-Komponente.

Wenn Sie einige Daten in den Redux-Status versetzen, sollten Sie diese Daten immer aus dem Redux-Status lesen. Für alles, was sich im Redux-Repository befindet, sollte dieses Repository die einzige Quelle für zuverlässige Daten sein.

Wenn nötig, ist es völlig normal, alles in einen Redux-Zustand zu versetzen. Möglicherweise wirkt sich dies auf die Leistung aus, wenn Sie Statusfragmente verwenden, die häufig aktualisiert werden müssen, oder wenn Sie über das Speichern des Status einer Komponente sprechen, in der abhängige Statusfragmente häufig verwendet werden. Sie sollten sich keine Sorgen um die Leistung machen, bis es Probleme mit der Leistung gibt. Wenn Sie jedoch über das Leistungsproblem besorgt sind, versuchen Sie beide Arten der Zusammenarbeit mit dem Staat und bewerten Sie deren Auswirkungen auf die Leistung. Profilieren Sie Ihr Projekt und merken Sie sich das RAIL-Leistungsmodell.

▍ Muss ich die Connect-Funktion von React-Redux verwenden oder ist es besser, Hooks zu verwenden?


Es kommt auf viel an. Die connect erstellt eine Komponente höherer Ordnung, die für die wiederholte Verwendung geeignet ist, und Hooks sind für die Integration mit einer einzelnen Komponente optimiert.

Muss ich dieselben Eigenschaften mit verschiedenen Komponenten verbinden? Wenn ja, verwenden Sie connect . Ansonsten würde ich lieber Haken pflücken. Stellen Sie sich beispielsweise vor, Sie haben eine Komponente, die für die Autorisierung von Berechtigungen für Benutzeraktionen verantwortlich ist:

 import { connect } from 'react-redux'; import RequiresPermission from './requires-permission-component'; import { userHasPermission } from '../../features/user-profile/user-profile-reducer'; import curry from 'lodash/fp/curry'; const requiresPermission = curry(  (NotPermittedComponent, { permission }, PermittedComponent) => {    const mapStateToProps = state => ({      NotPermittedComponent,      PermittedComponent,      isPermitted: userHasPermission(state, permission),    });    return connect(mapStateToProps)(RequiresPermission);  }, ); export default requiresPermission; 

Wenn ein Administrator intensiv mit der Anwendung arbeitet, für deren Aktionen alle besondere Berechtigungen erforderlich sind, können Sie eine Komponente höherer Ordnung erstellen, die alle diese Berechtigungen mit allen erforderlichen End-to-End-Funktionen kombiniert:

 import NextError from 'next/error'; import compose from 'lodash/fp/compose'; import React from 'react'; import requiresPermission from '../requires-permission'; import withFeatures from '../with-features'; import withAuth from '../with-auth'; import withEnv from '../with-env'; import withLoader from '../with-loader'; import withLayout from '../with-layout'; export default compose(  withEnv,  withAuth,  withLoader,  withLayout(),  withFeatures,  requiresPermission(() => <NextError statusCode={404} />, {    permission: 'admin',  }), ); 

So verwenden Sie es:

 import compose from 'lodash/fp/compose'; import adminPage from '../HOCs/admin-page'; import AdminIndex from '../features/admin-index/admin-index-component.js'; export default adminPage(AdminIndex); 

Die Komponenten-API höherer Ordnung ist für diese Aufgabe geeignet. Sie können es prägnanter lösen, indem Sie weniger Code als Hooks verwenden. Um die connect können, müssen Sie jedoch berücksichtigen, dass mapStateToProps als erstes Argument und mapStateToProps als zweites Argument verwendet werden. Wir dürfen nicht vergessen, dass diese Funktion Funktionen oder Objektliterale annehmen kann. Sie müssen wissen, wie sich die verschiedenen Verwendungszwecke von connect unterscheiden und dass dies eine Curry-Funktion ist, deren Currying jedoch nicht automatisch durchgeführt wird.

Mit anderen Worten, ich kann sagen, dass ich glaube, dass während der Entwicklung von connect viel Arbeit in Richtung der Prägnanz des Codes geleistet wurde, aber der resultierende Code ist weder besonders lesbar noch besonders praktisch. Wenn ich nicht mit mehreren Komponenten arbeiten muss, ziehe ich die unbequeme connect API gerne einer viel bequemeren Hook-API vor, auch wenn dies zu einer Erhöhung der Codemenge führt.

▍ Wenn ein Singleton als Anti-Pattern und Redux als Singleton betrachtet wird, bedeutet dies, dass Redux ein Anti-Pattern ist?


Nein, das tut es nicht. Die Verwendung eines Singletons im Code weist auf die zweifelhafte Qualität dieses Codes hin und weist auf das Vorhandensein eines gemeinsamen veränderlichen Zustands hin. Dies ist ein echtes Anti-Muster. Redux hingegen verhindert die Mutation des gemeinsam genutzten Status durch Kapselung (Sie sollten den Status der Anwendung nicht direkt außerhalb von Reduzierern ändern; Redux löst das Problem der Statusänderung) und durch Senden von Nachrichten (nur das gesendete Ereignisobjekt kann eine Statusänderung verursachen).

Zusammenfassung


Ersetzt Redux React-Haken? Haken sind großartig, aber sie ersetzen Redux nicht.

Wir hoffen, dass dieses Material Ihnen bei der Auswahl eines Zustandsverwaltungsmodells für Ihre React-Projekte hilft.

Liebe Leser! Haben Sie Situationen erlebt, in denen React-Hooks Redux ersetzen können?

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


All Articles