Ich traf Michael Jackson zum ersten Mal auf der React Rally 2016, kurz nachdem ich einen Artikel über React Router 3 geschrieben hatte. Michael ist einer der Hauptautoren von React Router, zusammen mit Ryan Florence. Es war aufregend, jemanden zu treffen, der ein Werkzeug gebaut hatte, das mir so gut gefiel, aber ich war schockiert, als er sagte: „Lassen Sie mich Ihnen unsere Ideen für React Router 4 zeigen, es ist ganz anders!“ Ehrlich gesagt, verstand ich die neue Richtung nicht und warum sie so große Änderungen benötigte. Da der Router ein so wichtiger Teil der Architektur einer Anwendung ist, würde dies potenziell einige Muster ändern, die ich lieb gewonnen hatte. Die Idee dieser Änderungen bereitete mir Angst. Angesichts des Gemeinschaftszusammenhalts und der Tatsache, dass React Router eine riesige Rolle in so vielen React-Anwendungen spielt, wusste ich nicht, wie die Community die Änderungen aufnehmen würde.
Ein paar Monate später wurde React Router 4 veröffentlicht, und allein aus dem Twitter-Buzz konnte ich erkennen, dass es gemischte Gefühle über das drastische Rewrite gab. Es erinnerte mich an den Widerstand, den die erste Version von React Router für ihre progressiven Konzepte erfahren hatte. In gewisser Weise ähnelten frühere Versionen von React Router unserem traditionellen Mentalitätsmodell dessen, was ein Anwendungsrouter „sein sollte“, indem alle Routenregeln an einem Ort zusammengefasst wurden. Die Verwendung von verschachteltem JSX für Routen wurde jedoch nicht von allen akzeptiert. Aber so wie JSX selbst seine Kritiker (zumindest die meisten) überwand, kamen viele dazu, zu glauben, dass ein verschachtelter JSX-Router eine ziemlich coole Idee war.
Also lernte ich React Router 4. Zugegebenermaßen war es am ersten Tag ein Kampf. Der Kampf lag nicht an der API, sondern eher an den Mustern und der Strategie für deren Verwendung. Mein Mentalitätsmodell für die Verwendung von React Router 3 ließ sich nicht gut auf v4 übertragen. Ich müsste meine Denkweise über die Beziehung zwischen dem Router und den Layout-Komponenten ändern, wenn ich erfolgreich sein wollte. Schließlich tauchten neue Muster auf, die für mich Sinn ergaben, und ich war sehr zufrieden mit der neuen Richtung des Routers. React Router 4 erlaubte mir, alles zu tun, was ich mit v3 konnte, und mehr. Außerdem habe ich anfangs die Verwendung von v4 überkompliziert. Als ich ein neues Mentalitätsmodell dafür entwickelte, erkannte ich, dass diese neue Richtung erstaunlich ist!
Meine Absicht für diesen Artikel ist nicht, die bereits gut geschriebene Dokumentation für React Router 4 zu wiederholen. Ich werde die gängigsten API-Konzepte abdecken, aber der eigentliche Fokus liegt auf Mustern und Strategien, die sich für mich als erfolgreich erwiesen haben.
React Router 5 ist jetzt verfügbar und abwärtskompatibel mit React Router 4. Es enthält hauptsächlich Fehlerbehebungen und interne Verbesserungen, um es kompatibler mit React 16 zu machen.
Hier sind einige JavaScript-Konzepte, mit denen Sie für diesen Artikel vertraut sein müssen
- React (stateless) Funktionale Komponenten
- ES2015 Pfeilfunktionen und ihre „impliziten Rückgaben“
- ES2015 Destrukturierung
- ES2015 Template-Literale
Wenn Sie zu den Typen gehören, die es vorziehen, direkt zu einer funktionierenden Demo zu springen, hier ist sie
Eine neue API und ein neues Mentalitätsmodell
Frühere Versionen von React Router zentralisierten die Routenregeln an einem Ort und hielten sie von Layout-Komponenten getrennt. Sicher, der Router konnte partitioniert und in mehrere Dateien organisiert werden, aber konzeptionell war der Router eine Einheit und im Grunde eine verschönerte Konfigurationsdatei.
Vielleicht ist der beste Weg, um zu sehen, wie sich v4 unterscheidet, eine einfache Zwei-Seiten-App in jeder Version zu schreiben und zu vergleichen. Die Beispiel-App hat nur zwei Routen für eine Startseite und eine Benutzerseite.
Hier ist es in v3
import { Router, Route, IndexRoute } from 'react-router'
const PrimaryLayout = props => (
<div className="primary-layout">
<header>
Our React Router 3 App
</header>
<main>
{props.children}
</main>
</div>
)
const HomePage =() => <div>Home Page</div>
const UsersPage = () => <div>Users Page</div>
const App = () => (
<Router history={browserHistory}>
<Route path="/" component={PrimaryLayout}>
<IndexRoute component={HomePage} />
<Route path="/users" component={UsersPage} />
</Route>
</Router>
)
render(<App />, document.getElementById('root'))
Hier sind einige Schlüsselkonzepte in v3, die in v4 nicht mehr gelten
- Der Router ist zentralisiert an einem Ort.
- Layout- und Seitenverschachtelung wird durch die Verschachtelung von
<Route>-Komponenten abgeleitet. - Layout- und Seitenkomponenten sind völlig naiv, dass sie Teil eines Routers sind.
React Router 4 befürwortet keinen zentralisierten Router mehr. Stattdessen leben Routenregeln innerhalb des Layouts und inmitten der Benutzeroberfläche selbst. Als Beispiel hier die gleiche Anwendung in v4
import { BrowserRouter, Route } from 'react-router-dom'
const PrimaryLayout = () => (
<div className="primary-layout">
<header>
Our React Router 4 App
</header>
<main>
<Route path="/" exact component={HomePage} />
<Route path="/users" component={UsersPage} />
</main>
</div>
)
const HomePage =() => <div>Home Page</div>
const UsersPage = () => <div>Users Page</div>
const App = () => (
<BrowserRouter>
<PrimaryLayout />
</BrowserRouter>
)
render(<App />, document.getElementById('root'))
Neues API-Konzept: Da unsere App für den Browser gedacht ist, müssen wir sie in <BrowserRouter> einschließen, das von v4 stammt. Beachten Sie auch, dass wir jetzt von react-router-dom importieren (was bedeutet, dass wir npm install react-router-dom und nicht react-router installieren). Hinweis! Es heißt jetzt react-router-dom, weil es auch eine native Version gibt.
Das erste, was auffällt, wenn man eine mit React Router v4 erstellte App betrachtet, ist, dass der „Router“ zu fehlen scheint. In v3 war der Router dieses riesige Ding, das wir direkt an das DOM gerendert haben und das unsere Anwendung orchestriert hat. Jetzt, abgesehen von <BrowserRouter>, ist das erste, was wir ins DOM werfen, unsere Anwendung selbst.
Ein weiterer v3-Standard, der im v4-Beispiel fehlt, ist die Verwendung von {props.children} zum Verschachteln von Komponenten. Das liegt daran, dass in v4 überall dort, wo die <Route>-Komponente geschrieben ist, die Unterkomponente gerendert wird, wenn die Route übereinstimmt.
Inklusives Routing
Im vorherigen Beispiel haben Sie vielleicht die exact-Prop bemerkt. Was hat es damit auf sich? V3-Routenregeln waren „exklusiv“, was bedeutete, dass nur eine Route gewann. V4-Routen sind standardmäßig „inklusiv“, was bedeutet, dass mehr als eine <Route> gleichzeitig übereinstimmen und gerendert werden kann.
Im vorherigen Beispiel versuchen wir, entweder die HomePage oder die UsersPage abhängig vom Pfad zu rendern. Wenn die exact-Prop aus dem Beispiel entfernt worden wäre, wären sowohl die HomePage- als auch die UsersPage-Komponenten gleichzeitig gerendert worden, wenn Sie `/users` im Browser besuchen.
Um die Abgleichlogik besser zu verstehen, überprüfen Sie path-to-regexp, das v4 nun verwendet, um zu bestimmen, ob Routen mit der URL übereinstimmen.
Um zu demonstrieren, wie inklusives Routing hilfreich ist, fügen wir der Kopfzeile ein UserMenu hinzu, aber nur, wenn wir uns im Benutzerbereich unserer Anwendung befinden
const PrimaryLayout = () => (
<div className="primary-layout">
<header>
Our React Router 4 App
<Route path="/users" component={UsersMenu} />
</header>
<main>
<Route path="/" exact component={HomePage} />
<Route path="/users" component={UsersPage} />
</main>
</div>
)
Wenn der Benutzer nun `/users` besucht, werden beide Komponenten gerendert. So etwas war in v3 mit bestimmten Mustern machbar, aber es war schwieriger. Dank der inklusiven Routen von v4 ist es jetzt ein Kinderspiel.
Exklusives Routing
Wenn Sie nur möchten, dass eine Route in einer Gruppe übereinstimmt, verwenden Sie <Switch>, um exklusives Routing zu aktivieren
const PrimaryLayout = () => (
<div className="primary-layout">
<PrimaryHeader />
<main>
<Switch>
<Route path="/" exact component={HomePage} />
<Route path="/users/add" component={UserAddPage} />
<Route path="/users" component={UsersPage} />
<Redirect to="/" />
</Switch>
</main>
</div>
)
Nur eine der Routen in einem gegebenen <Switch> wird gerendert. Wir benötigen trotzdem exact für die HomePage-Route, wenn wir sie zuerst auflisten. Andernfalls würde die Startseitenroute übereinstimmen, wenn Pfade wie `/users` oder `/users/add` besucht werden. Tatsächlich ist die strategische Platzierung das A und O, wenn man eine exklusive Routing-Strategie verwendet (wie es bei traditionellen Routern immer der Fall war). Beachten Sie, dass wir die Routen für /users/add strategisch vor /users platzieren, um die korrekte Übereinstimmung zu gewährleisten. Da der Pfad /users/add für `/users` und `/users/add` übereinstimmen würde, ist es am besten, /users/add zuerst zu setzen.
Sicher, wir könnten sie in beliebiger Reihenfolge setzen, wenn wir exact auf bestimmte Weise verwenden, aber zumindest haben wir Optionen.
Die Komponente <Redirect> führt immer eine Browser-Umleitung durch, wenn sie angetroffen wird, aber wenn sie sich in einer <Switch>-Anweisung befindet, wird die Redirect-Komponente nur gerendert, wenn keine anderen Routen zuerst übereinstimmen. Um zu sehen, wie <Redirect> in einem nicht-switch-Umstand verwendet werden könnte, siehe Autorisierte Route unten.
„Index-Routen“ und „Nicht gefunden“
Während es in v4 keine <IndexRoute> mehr gibt, erreicht die Verwendung von <Route exact> dasselbe. Oder wenn keine Routen aufgelöst wurden, verwenden Sie <Switch> mit <Redirect>, um zu einer Standardseite mit einem gültigen Pfad (wie ich es in der HomePage im Beispiel getan habe) oder sogar zu einer Seite „Nicht gefunden“ umzuleiten.
Verschachtelte Layouts
Sie ahnen wahrscheinlich schon verschachtelte Unterlayouts und wie Sie diese erreichen könnten. Ich dachte nicht, dass ich mit diesem Konzept kämpfen würde, aber ich tat es. React Router v4 gibt uns viele Optionen, was es leistungsfähig macht. Optionen bedeuten jedoch die Freiheit, Strategien zu wählen, die nicht ideal sind. Oberflächlich betrachtet sind verschachtelte Layouts trivial, aber je nach Ihren Entscheidungen können Sie aufgrund der Art und Weise, wie Sie den Router organisiert haben, Reibungsverluste erfahren.
Um dies zu demonstrieren, stellen wir uns vor, wir möchten unseren Benutzerbereich erweitern, sodass wir eine Seite „Benutzer durchsuchen“ und eine Seite „Benutzerprofil“ haben. Wir benötigen auch ähnliche Seiten für Produkte. Benutzer und Produkte benötigen beide Unterlayouts, die speziell und einzigartig für jeden entsprechenden Bereich sind. Zum Beispiel könnten sie unterschiedliche Navigationsregisterkarten haben. Es gibt einige Ansätze, um dies zu lösen, einige gut und einige schlecht. Der erste Ansatz ist nicht sehr gut, aber ich möchte ihn Ihnen zeigen, damit Sie nicht in diese Falle tappen. Der zweite Ansatz ist viel besser.
Für den ersten modifizieren wir unser PrimaryLayout, um die Seiten für das Durchsuchen und die Profile für Benutzer und Produkte unterzubringen
const PrimaryLayout = props => {
return (
<div className="primary-layout">
<PrimaryHeader />
<main>
<Switch>
<Route path="/" exact component={HomePage} />
<Route path="/users" exact component={BrowseUsersPage} />
<Route path="/users/:userId" component={UserProfilePage} />
<Route path="/products" exact component={BrowseProductsPage} />
<Route path="/products/:productId" component={ProductProfilePage} />
<Redirect to="/" />
</Switch>
</main>
</div>
)
}
Während dies technisch funktioniert, enthüllt ein genauerer Blick auf die beiden Benutzerseiten das Problem
const BrowseUsersPage = () => (
<div className="user-sub-layout">
<aside>
<UserNav />
</aside>
<div className="primary-content">
<BrowseUserTable />
</div>
</div>
)
const UserProfilePage = props => (
<div className="user-sub-layout">
<aside>
<UserNav />
</aside>
<div className="primary-content">
<UserProfile userId={props.match.params.userId} />
</div>
</div>
)
Neues API-Konzept: props.match wird jeder Komponente übergeben, die von <Route> gerendert wird. Wie Sie sehen können, wird der userId von props.match.params bereitgestellt. Mehr dazu in der v4-Dokumentation. Alternativ, wenn eine Komponente Zugriff auf props.match benötigt, aber die Komponente nicht direkt von einer <Route> gerendert wurde, können wir die withRouter() Higher-Order-Komponente verwenden.
Jede Benutzerseite rendert nicht nur ihren jeweiligen Inhalt, sondern muss sich auch um das Unterlayout selbst kümmern (und das Unterlayout wird für jeden wiederholt). Obwohl dieses Beispiel klein ist und trivial erscheinen mag, kann wiederholter Code in einer echten Anwendung ein Problem darstellen. Ganz zu schweigen davon, dass jedes Mal, wenn BrowseUsersPage oder UserProfilePage gerendert wird, eine neue Instanz von UserNav erstellt wird, was bedeutet, dass alle ihre Lifecycle-Methoden von vorne beginnen. Hätte die Navigationsregisterkarte anfänglichen Netzwerkverkehr erfordert, hätte dies unnötige Anfragen verursacht – alles wegen der Art und Weise, wie wir uns entschieden haben, den Router zu verwenden.
Hier ist ein anderer Ansatz, der besser ist
const PrimaryLayout = props => {
return (
<div className="primary-layout">
<PrimaryHeader />
<main>
<Switch>
<Route path="/" exact component={HomePage} />
<Route path="/users" component={UserSubLayout} />
<Route path="/products" component={ProductSubLayout} />
<Redirect to="/" />
</Switch>
</main>
</div>
)
}
Anstatt vier Routen, die jeder Seite für Benutzer und Produkte entsprechen, haben wir stattdessen zwei Routen für das Layout jedes Bereichs.
Beachten Sie, dass die obigen Routen die exact-Prop nicht mehr verwenden, da wir möchten, dass /users mit jeder Route übereinstimmt, die mit /users beginnt, und ähnlich für Produkte.
Mit dieser Strategie wird es zur Aufgabe der Unterlayouts, zusätzliche Routen zu rendern. So könnte UserSubLayout aussehen
const UserSubLayout = () => (
<div className="user-sub-layout">
<aside>
<UserNav />
</aside>
<div className="primary-content">
<Switch>
<Route path="/users" exact component={BrowseUsersPage} />
<Route path="/users/:userId" component={UserProfilePage} />
</Switch>
</div>
</div>
)
Der offensichtlichste Vorteil der neuen Strategie ist, dass das Layout nicht über alle Benutzerseiten hinweg wiederholt wird. Es ist auch ein doppelter Gewinn, da es nicht die gleichen Lifecycle-Probleme wie im ersten Beispiel gibt.
Eine Sache, die man beachten sollte, ist, dass, obwohl wir tief in unserer Layout-Struktur verschachtelt sind, die Routen immer noch ihren vollständigen Pfad identifizieren müssen, um übereinzustimmen. Um sich das repetitive Tippen zu ersparen (und falls Sie das Wort „users“ in etwas anderes ändern), verwenden Sie stattdessen props.match.path
const UserSubLayout = props => (
<div className="user-sub-layout">
<aside>
<UserNav />
</aside>
<div className="primary-content">
<Switch>
<Route path={props.match.path} exact component={BrowseUsersPage} />
<Route path={`${props.match.path}/:userId`} component={UserProfilePage} />
</Switch>
</div>
</div>
)
Match
Wie wir bisher gesehen haben, ist props.match nützlich, um zu wissen, welchen userId das Profil rendert, und auch zum Schreiben unserer Routen. Das match-Objekt gibt uns mehrere Eigenschaften, darunter match.params, match.path, match.url und mehrere weitere.
match.path vs match.url
Die Unterschiede zwischen diesen beiden können zunächst unklar erscheinen. Das Konsolenausgabe kann manchmal die gleichen Ergebnisse liefern, wodurch ihre Unterschiede noch unklarer werden. Zum Beispiel werden beide Konsolenausgaben denselben Wert ausgeben, wenn der Browserpfad `/users` ist
const UserSubLayout = ({ match }) => {
console.log(match.url) // output: "/users"
console.log(match.path) // output: "/users"
return (
<div className="user-sub-layout">
<aside>
<UserNav />
</aside>
<div className="primary-content">
<Switch>
<Route path={match.path} exact component={BrowseUsersPage} />
<Route path={`${match.path}/:userId`} component={UserProfilePage} />
</Switch>
</div>
</div>
)
}
ES2015-Konzept: match wird auf der Parameterebene der Komponentenfunktion destrukturiert. Das bedeutet, dass wir match.path anstelle von props.match.path schreiben können.
Obwohl wir den Unterschied noch nicht sehen können, ist match.url der tatsächliche Pfad in der Browser-URL und match.path der Pfad, der für den Router geschrieben wurde. Deshalb sind sie gleich, zumindest bisher. Wenn wir jedoch dieselben Konsolenausgaben eine Ebene tiefer in UserProfilePage durchführen und `/users/5` im Browser besuchen, wäre match.url "/users/5" und match.path wäre "/users/:userId".
Welchen wählen?
Wenn Sie einen davon verwenden möchten, um Ihre Routenpfade zu erstellen, fordere ich Sie auf, match.path zu wählen. Die Verwendung von match.url zum Erstellen von Routenpfaden führt schließlich zu einem Szenario, das Sie nicht wollen. Hier ist ein Szenario, das mir passiert ist. Innerhalb einer Komponente wie UserProfilePage (die gerendert wird, wenn der Benutzer `/users/5` besucht), habe ich Unterkomponenten wie diese gerendert
const UserComments = ({ match }) => (
<div>UserId: {match.params.userId}</div>
)
const UserSettings = ({ match }) => (
<div>UserId: {match.params.userId}</div>
)
const UserProfilePage = ({ match }) => (
<div>
User Profile:
<Route path={`${match.url}/comments`} component={UserComments} />
<Route path={`${match.path}/settings`} component={UserSettings} />
</div>
)
Um das Problem zu veranschaulichen, rendere ich zwei Unterkomponenten, wobei ein Routenpfad aus match.url und einer aus match.path erstellt wird. Hier ist, was passiert, wenn Sie diese Seiten im Browser besuchen
- Wenn Sie `/users/5/comments` besuchen, wird „UserId: undefined“ gerendert.
- Wenn Sie `/users/5/settings` besuchen, wird „UserId: 5“ gerendert.
Warum funktioniert match.path, um unsere Pfade zu erstellen, und match.url nicht? Die Antwort liegt in der Tatsache, dass {${match.url}/comments} im Wesentlichen dasselbe ist, als hätte ich {'/users/5/comments'} hartkodiert. Dies bedeutet, dass die nachfolgende Komponente match.params nicht korrekt ausfüllen kann, da keine Parameter im Pfad vorhanden waren, nur eine hartkodierte 5.
Erst später sah ich diesen Teil der Dokumentation und erkannte, wie wichtig er war
match
- path – (string) Das Muster des Pfades, das abgeglichen werden soll. Nützlich für das Erstellen verschachtelter
<Route>s- url – (string) Der abgeglichene Teil der URL. Nützlich für das Erstellen verschachtelter
<Link>s
Vermeidung von Übereinstimmungs-Kollisionen
Nehmen wir an, die App, die wir erstellen, ist ein Dashboard, so dass wir Benutzer hinzufügen und bearbeiten können, indem wir `/users/add` und `/users/5/edit` besuchen. Aber mit den vorherigen Beispielen verweist users/:userId bereits auf eine UserProfilePage. Bedeutet das, dass die Route mit users/:userId nun auf eine weitere Unter-Unter-Layouts verweisen muss, um Bearbeitungen und das Profil unterzubringen? Ich glaube nicht. Da sowohl die Bearbeitungs- als auch die Profilseite dasselbe Benutzer-Unterlayout teilen, funktioniert diese Strategie gut.
const UserSubLayout = ({ match }) => (
<div className="user-sub-layout">
<aside>
<UserNav />
</aside>
<div className="primary-content">
<Switch>
<Route exact path={props.match.path} component={BrowseUsersPage} />
<Route path={`${match.path}/add`} component={AddUserPage} />
<Route path={`${match.path}/:userId/edit`} component={EditUserPage} />
<Route path={`${match.path}/:userId`} component={UserProfilePage} />
</Switch>
</div>
</div>
)
Beachten Sie, dass die Routen zum Hinzufügen und Bearbeiten strategisch vor der Profilroute kommen, um die korrekte Übereinstimmung zu gewährleisten. Hätte der Profilpfad zuerst gestanden, wäre der Besuch von `/users/add` mit dem Profil übereingestimmt (da „add“ mit :userId übereinstimmen würde).
Alternativ können wir die Profilroute zuerst setzen, wenn wir den Pfad ${match.path}/:userId(\\d+) verwenden, der sicherstellt, dass :userId eine Zahl sein muss. Dann würde der Besuch von `/users/add` keinen Konflikt erzeugen. Diesen Trick habe ich in den Dokumenten zu path-to-regexp gelernt.
Autorisierte Route
Es ist in Anwendungen sehr üblich, die Fähigkeit des Benutzers, bestimmte Routen zu besuchen, je nach seinem Anmeldestatus einzuschränken. Ebenso üblich ist es, ein „Look-and-Feel“ für die nicht autorisierten Seiten (wie „Anmelden“ und „Passwort vergessen“) im Vergleich zum „Look-and-Feel“ für die autorisierten Seiten (der Hauptteil der Anwendung) zu haben. Um jede dieser Anforderungen zu erfüllen, betrachten Sie diesen Haupteinstiegspunkt einer Anwendung
class App extends React.Component {
render() {
return (
<Provider store={store}>
<BrowserRouter>
<Switch>
<Route path="/auth" component={UnauthorizedLayout} />
<AuthorizedRoute path="/app" component={PrimaryLayout} />
</Switch>
</BrowserRouter>
</Provider>
)
}
}
Die Verwendung von react-redux funktioniert mit React Router v4 sehr ähnlich wie zuvor, wickeln Sie einfach <BrowserRouter> in <Provider> ein und alles ist eingerichtet.
Es gibt ein paar Erkenntnisse aus diesem Ansatz. Das erste ist, dass ich zwischen zwei Top-Level-Layouts wähle, je nachdem, in welchem Bereich der Anwendung wir uns befinden. Das Besuchen von Pfaden wie `/auth/login` oder `/auth/forgot-password` verwendet das UnauthorizedLayout – eines, das für diese Kontexte passend aussieht. Wenn der Benutzer angemeldet ist, stellen wir sicher, dass alle Pfade ein `/app`-Präfix haben, das AuthorizedRoute verwendet, um zu bestimmen, ob der Benutzer angemeldet ist oder nicht. Wenn der Benutzer versucht, eine Seite mit `/app` zu besuchen und nicht angemeldet ist, wird er zur Anmeldeseite weitergeleitet.
AuthorizedRoute ist jedoch kein Teil von v4. Ich habe es selbst mit Hilfe der v4-Dokumentation erstellt. Ein erstaunliches neues Feature in v4 ist die Möglichkeit, eigene Routen für spezielle Zwecke zu erstellen. Anstatt eine component-Prop an <Route> zu übergeben, übergeben Sie stattdessen einen render-Callback
class AuthorizedRoute extends React.Component {
componentWillMount() {
getLoggedUser()
}
render() {
const { component: Component, pending, logged, ...rest } = this.props
return (
<Route {...rest} render={props => {
if (pending) return <div>Loading...</div>
return logged
? <Component {...this.props} />
: <Redirect to="/auth/login" />
}} />
)
}
}
const stateToProps = ({ loggedUserState }) => ({
pending: loggedUserState.pending,
logged: loggedUserState.logged
})
export default connect(stateToProps)(AuthorizedRoute)
Während Ihre Anmeldestrategie von meiner abweichen mag, verwende ich eine Netzwerkanforderung, um getLoggedUser() abzurufen und pending und logged in den Redux-Status einzufügen. pending bedeutet nur, dass die Anfrage noch in Bearbeitung ist.
Klicken Sie hier, um ein voll funktionsfähiges Authentifizierungsbeispiel auf CodePen anzuzeigen.

Andere Erwähnungen
Es gibt viele weitere coole Aspekte von React Router v4. Um zu einem Abschluss zu kommen, erwähnen wir ein paar kleine Dinge, damit sie Sie nicht überraschen.
<Link> vs <NavLink>
In v4 gibt es zwei Möglichkeiten, ein Anker-Tag in den Router zu integrieren: <Link> und <NavLink>
<NavLink> funktioniert wie <Link>, bietet aber zusätzliche Styling-Möglichkeiten, je nachdem, ob die <NavLink> mit der Browser-URL übereinstimmt. Zum Beispiel gibt es in der Beispielanwendung eine <PrimaryHeader>-Komponente, die so aussieht
const PrimaryHeader = () => (
<header className="primary-header">
<h1>Welcome to our app!</h1>
<nav>
<NavLink to="/app" exact activeClassName="active">Home</NavLink>
<NavLink to="/app/users" activeClassName="active">Users</NavLink>
<NavLink to="/app/products" activeClassName="active">Products</NavLink>
</nav>
</header>
)
Die Verwendung von <NavLink> ermöglicht es mir, der aktiven Verknüpfung eine Klasse namens active zuzuweisen. Aber beachten Sie auch, dass ich exact auch hier verwenden kann. Ohne exact wäre der Link zur Startseite aktiv, wenn `/app/users` besucht wird, aufgrund der inklusiven Matching-Strategien von v4. In meinen persönlichen Erfahrungen ist <NavLink> mit der Option exact wesentlich stabiler als das Äquivalent von v3 <Link>.
URL-Query-Strings
Es gibt keine Möglichkeit mehr, den Query-String einer URL aus React Router v4 zu erhalten. Mir scheint, dass die Entscheidung getroffen wurde, da es keinen Standard dafür gibt, wie mit komplexen Query-Strings umzugehen ist. Anstatt dass v4 eine Meinung in das Modul einbaut, haben sie sich entschieden, den Entwickler entscheiden zu lassen, wie mit Query-Strings umgegangen werden soll. Das ist eine gute Sache.
Persönlich verwende ich query-string, das vom immer großartigen sindresorhus stammt.
Dynamische Routen
Einer der besten Teile von v4 ist, dass fast alles (einschließlich <Route>) nur eine React-Komponente ist. Routen sind keine magischen Dinge mehr. Wir können sie bedingt rendern, wann immer wir wollen. Stellen Sie sich vor, ein ganzer Bereich Ihrer Anwendung ist verfügbar, um dorthin zu routen, wenn bestimmte Bedingungen erfüllt sind. Wenn diese Bedingungen nicht erfüllt sind, können wir Routen entfernen. Wir können sogar einige verrückte coole rekursive Routen machen.
React Router 4 ist einfacher, weil es nur Komponenten™ ist
Dieser Artikel ist großartig, ich hatte Schwierigkeiten, den neuen Ansatz von v4 zu verstehen. Ich mag es wirklich, dass diese Änderungen im Grunde die Notwendigkeit eliminieren, Dinge wie
Kann es kaum erwarten, es auszuprobieren. Danke Brad.
Eine Sache, bei der ich mir immer noch nicht sicher bin, wie ich sie in React Router v4 wirklich machen soll, ist, wie man programmatisch zu einer neuen Route weiterleitet, wenn etwas keine React-Komponente ist. Vielleicht denke ich mit Version 4 einfach nicht richtig.
Ein Beispiel ist, wie man mit der Umleitung zu einer authentifizierten Route umgeht, basierend auf der Änderung des Redux-Status. Oder wie man sicherstellt, dass ein Benutzer zur Anmeldeseite weitergeleitet wird, wenn er nicht authentifiziert ist, aber dann nach der Authentifizierung zum Verweis zurückkehrt.
Kannst du die History-API direkt dafür benutzen?
Ich benutze seit kurzem React Router 4, und das war genau das, was mich anfangs am meisten verwirrte. Es gibt zwei Möglichkeiten (soweit ich weiß). Der normalste und wahrscheinlich einfachste Weg ist wie Brad gesagt hat
Es gibt auch ein neues HOC namens . Um es programmatisch zu verwenden, müssen Sie es basierend auf einer Zustandsbedingung anzeigen. Etwas wie
Sie könnten natürlich Ihren Umleitungsstatus auch in Redux speichern, wenn Sie möchten…
Die zweite ist meiner Meinung nach etwas mühsam einzurichten, aber sie ermöglicht es Ihnen, ein Zustands-Objekt hinzuzufügen, das Sie wie einen Query-String verwenden könnten (RR4 behält den Query-String nicht bei).
Die wichtigste Falle, auf die ich bei Option 1 gestoßen bin, war, dass die Komponente entweder ein Kind einer Route sein muss oder Sie sie mit dem withRouter HOC umschließen müssen. Für mich konnte ich von meiner Kopfzeile oder Fußzeile aus nicht weiterleiten... die nicht in irgendeiner Route verschachtelt sind... bis ich das herausgefunden habe.
Das sieht ungefähr so aus
Der Artikel hat mir sehr gefallen, besonders wie fokussiert er darauf ist, die Hinweise in diesen kleinen „Hey“-Abschnitten zu halten.
Hallo,
Ich benutze react-router v4, habe aber einen Anwendungsfall, den ich nicht elegant lösen kann
Stellen Sie sich eine Route vor, die die App.js-Komponente rendert
Innerhalb dieser Komponente habe ich eine Kopfzeile, eine Fußzeile und eine Hauptkomponente. Die Hauptkomponente rendert eine Liste von Routen mit einem Switch. Bis hierher funktioniert es perfekt, aber ich muss in der Kopfzeile und der Kopfzeile Informationen aus den Match-Parametern kennen, die an die Switch-Route meines Kindes übergeben werden. Weil dort die Projekt-ID ist
Jetzt benutze ich matchpath, um alle Routen zu überprüfen und den Match zu erhalten
In v3 kennt die Elternroute die Parameter der Nachkommen, aber nicht in v4
Weißt du, wie man diesen Anwendungsfall löst?
Vielen Dank
Ich versuche, dem zu folgen, bin mir aber nicht ganz sicher, ob ich es zu 100 % verstehe. Ich weiß, dass es wahrscheinlich eine komplexe Situation ist, so dass es nicht so einfach ist, ein Codepen dafür zu erstellen. Aber ich glaube, ich habe die Lösung. Es klingt, als wäre die Kopfzeile nicht von einer Route gerendert worden, also, wie Sie vielleicht wissen, haben nur Komponenten, die von Routen gerendert werden, Props wie
match. Komponenten, die Zugriff auf Props wiematch,historyusw. von v4 benötigen und nicht von einer Route gerendert wurden, können diewithRouterHigher-Order-Komponente wie folgt verwendenToller Artikel, Mann. Hat ihn geliebt. Danke. :)
Vielen Dank, es ist großartig
Ein weiterer toller Artikel. Danke Brad.
Bester Routen-Tutorial, den ich bisher gesehen habe!
Toller Artikel, aber Sie erwähnen nicht die Entfernung der Hooks
onEnter,onChangeundonLeave. Dies macht es im Grunde unmöglich, eine Bestätigung oder etwas anderes zu haben, dem ein Benutzer zustimmen muss, bevor er eintritt, wenn der Entwickler funktionale Komponenten anstelle klassischer zustandsbehafteter Komponenten mitcomponentWillMountusw. verwendet.Die Aufnahme der Komponente
<Prompt />hilft, dieses Problem zu lösen, kann aber nicht wirklich gestylt werden und zeigt nur eine Benachrichtigung.Ein Großteil der negativen Publicity kommt von der Tatsache, dass diese Abschreibung viele Websites kaputt gemacht hat und es keine triviale Lösung gibt.
Ja, bestimmte Dinge sind weg. Ich weiß nicht über jeden Anwendungsfall Bescheid, aber der von Ihnen erwähnte (Zustimmung vor dem Betreten) könnte lösbar sein, indem man einfach seine eigene „Route-Wrapper“-Komponente erstellt. Entschuldigen Sie, ich habe heute nicht viel Zeit, aber hier ist ein Beispiel, wie ich einen Wrapper für Authentifizierungszwecke erstelle
Diese Route kann nun als
<AuthorizedRoute path="..." component="..."/>verwendet werden und dient als Bedingung, die bestimmt, ob Sie die Komponente aufrufen dürfen. Es scheint, als könnten Sie diese Art von Konzept an Ihre Bedürfnisse anpassen?Und ja, v4 wird Code, der versucht, Dinge auf v3-Weise zu tun, brechen. Ich kenne die genauen negativen Schlagzeilen, die Sie erwähnen, nicht, aber es ist eine Major-Version, was „Breaking Changes“ bedeutet. Ich denke also, Entwickler sollten sich dessen bewusst sein.
Das ist von Gilly Ames, nach Abpfiff