Blog

Ein paar Tricks mit Redux und React-Router

März 20, 2018
Haftungsausschluss: Dieser Artikel setzt Kenntnisse in React, Redux und React-Router voraus. Ein Beispiel ist in Typescript, weil ich Produktionscode teilen wollte.

Wir möchten Ihnen einige der Tricks vorstellen, mit denen wir die Entwicklung schneller und einfacher gestalten konnten.

Route mit Authentifizierung

Wie jede andere Webanwendung auch. Wir haben einige Routen, bei denen der Benutzer eingeloggt sein muss. Für diese verwenden wir unsere eigene benutzerdefinierte Route. Wir haben unsere Routenkomponente AuthorizedRoute genannt.

Die Verwendung von AuthorizedRoute ist dieselbe wie bei einer React-Router Route Component:

class AuthorizedRoute extends Component<stateprops &=”” dispatchprops=”” routeprops,=”” {}=””> {</stateprops>

public componentWillMount() {
if (!this.props.token) {
this.props.dispatchLogin();
}
}

public render() {
const { component: RouteComponent, render, token, …rest } = this.props;
if (!this.props.token) {
return <route {=”” …rest=”” }=”” render=”{” this.renderfalse=””>;</route>
}
if (render) {
return <route {=”” …rest=”” }=”” render=”{“>;</route>
}
return <route {=”” …rest=”” }=”” component=”{” routecomponent=””>;</route>
}
private renderFalse = () => false;
}

interface StateProps {
token?: string;
}

const mapStateToProps = (state: AppState): StateProps => ({
token: tokenSelector(state),
});

interface DispatchProps {
dispatchLogin: () => LoginPromise;
push: (location: LocationDescriptor, state?: LocationState) => void;
}

const mapDispatchToProps = (dispatch: Dispatch) => ({
dispatchLogin: () => dispatch(login()),
});

export default connect<stateprops, dispatchprops,=”” routeprops=””>(mapStateToProps, mapDispatchToProps)(AuthorizedRoute);</stateprops,>

Wie Sie sehen können, verwendet AuthorizedRoute die Route-Komponente für die Darstellung. Wir entscheiden jedoch, was wir in der Route-Komponente darstellen. In diesem speziellen Fall interessiert uns, ob der Benutzer eingeloggt ist oder nicht. Wir wissen, ob der Benutzer eingeloggt ist, indem wir das Token im Redux-Status überprüfen.

Und nicht nur das: Wenn der Benutzer nicht angemeldet ist, senden wir auch eine Aktion, die die Anmeldung auslöst. In unserem speziellen Fall wird der Benutzer zur Anmeldeseite weitergeleitet.

Durch die Kombination von React Router und Redux können wir ganz einfach Routen hinzufügen, die eine Authentifizierung benötigen, ohne zusätzlichen Code.

Layout der Seite

In der Onedot-App haben die meisten Seiten ein gemeinsames Layout. Sie haben die gleiche Seitenleiste, Kopfzeile, Positionierung in der Mitte der Seite, usw.

Daher werden alle Routes, die dasselbe Layout verwenden, mit einer Layout-Komponente umhüllt. Mit React Router können Sie dies für diese Situationen tun:

<layout></layout>
<switch></switch>
<route exact=”” path=”/files” component=”{” datafiles=”” }=””></route>
<route exact=”” path=”/files/:fileId” component=”{” datafile=”” }=””></route>

Das ist gut so. Alle Seiten haben jetzt das gleiche Layout.

Wir sind etwas weiter gegangen und haben in unserem Layout auch Benachrichtigungen und Overlays. Benachrichtigungen sind die üblichen Meldungen, nachdem etwas erreicht wurde oder fehlgeschlagen ist: Ihre Datei cool_data.csv wurde entfernt. Overlays sind Modals, die vom Benutzer erwarten, dass er eine bestimmte Aktion ausführt. Zum Beispiel, wenn ein Ordner erstellt wird und der Benutzer den Namen eingeben muss.

Die Benachrichtigungen und Overlays werden durch Redux-Aktionen ausgelöst. Sie sind also Container, die mit Redux verbunden sind. Wir haben einen Teil des Zustands, der aktive Benachrichtigungen und Overlays verwaltet.

Dies ist möglich, weil der Provider von React Redux ein Elternteil des vorherigen Codes ist.

Wir haben jedoch festgestellt, dass wir in den Overlays die Informationen über den Parameter der URL benötigen. Den Parameter legen Sie fest, wenn Sie die Route erstellen:

<route exact=”” path=”/files/:fileId” component=”{” datafile=”” }=””></route>

Einige unserer Overlays mussten den Wert des Parameters fileId kennen.

Unser erster Ansatz war, es zu dem Teil des Zustands in Redux hinzuzufügen, der das Overlay auslöst. Das war ärgerlich. Was auch immer das Overlay auslöste, musste diese Daten haben. Entweder, weil es direkt mit dem React Router mit withRouter verbunden war oder weil der Parent die Daten als Prop übergab.

Wir haben dann versucht, das Overlay mit dem React Router direkt mit withRouter zu verbinden. Das verband die Komponente mit dem Router, aber wir hatten keine Informationen über den Pfad. Der Parameter fileId war nicht vorhanden. Nur die Kinder der Komponente in Route haben Zugriff auf Informationen über die spezifische Route. Erinnern Sie sich, dass Overlay im Layout war, das sich außerhalb der Route befand.

<layout></layout>
<switch></switch>
<route exact=”” path=”/files” component=”{” datafiles=”” }=””></route>
<route exact=”” path=”/files/:fileId” component=”{” datafile=”” }=””></route>

Eine Lösung könnte darin bestehen, die Overlays-Komponente zu jeder Route hinzuzufügen. Das hätte bedeutet, dass wir jedes Mal, wenn wir eine neue Route erstellen, die Overlays verwendet (was bei den meisten der Fall ist), daran denken müssten, sie hinzuzufügen. Das war keine gute Lösung.

Die Lösung, die wir gefunden haben, ist die Verwendung der Render-Methode in der Komponente Route. Anstatt die Komponenten-Prop zu übergeben, können Sie eine Render-Prop an Route übergeben. Schauen Sie sich die Render-Prop in ihrer Dokumentation an.

Anstatt also das Layout als übergeordnete Komponente von Switch. Wir haben uns für diese Lösung entschieden:

const renderWithLayout = (Component) => (props) => (
<layout {=”” …props=”” }=””></layout>
<component {=”” …props=”” }=””></component>

);

Dieses renderWithLayout wird in unseren Routes verwendet:

<switch></switch>

genau
path="/dateien"
render={ renderWithLayout(DataFiles) }
/>

exact
path=”/files/:fileId”
render={ renderWithLayout(DataFile) }
/>

Es ist erwähnenswert, dass das Layout jetzt nicht mehr den Schalter umhüllt, sondern die Komponente jeder Route umhüllt.

Wenn wir renderWithLayout(DataFiles) aufrufen, erhalten wir eine Funktion zurück, die vom React Router aufgerufen wird. Als Parameter werden alle Requisiten übergeben, die auch an die Komponente in der Prop-Komponente übergeben werden.

Aus den React Router Docs: Die Render-Prop erhält die gleichen Route-Prop wie die Render-Prop der Komponente.

const renderWithLayout = (Component) => (props) => (
<layout {=”” …props=”” }=””></layout>
<component {=”” …props=”” }=””></component>

);

Das bedeutet, dass unsere Layout-Komponente immer alle Informationen der Route Props erhält. Das bedeutet, dass wir endlich alle seine untergeordneten Komponenten (Overlays) mit dem React Router verbinden können und alle Daten aus dem Pfad erhalten. Genauer gesagt die Parameter der Route.

Schlussfolgerung

React Router und Redux sind für sich genommen mächtige Werkzeuge. Wenn man sie jedoch miteinander kombiniert, erhält man etwas, das noch leistungsfähiger ist als die Summe seiner Teile.

Dies sind nur 2 der von uns verwendeten Kombinationen. Welches Muster verwenden Sie? Wir würden uns freuen, Ihre Meinung zu diesem und anderen Mustern zu hören.

Weitere Informationen zu Redux-Mustern finden Sie in unserem Blogbeitrag Wie wir Redux mit unserer Services API verbinden. Vielen Dank für die Lektüre und schauen Sie sich unbedingt unsere offenen Stellen für Ingenieure an!