EOS TechLab

Hands-on React

Nils Hartmann | @nilshartmann

Vorbereitung

git clone https://github.com/nilshartmann/react-training

cd react-training/blog-example/workspace

npm install

Slides: https://nils.buzz/techlab

oder: react-training/2019_hands_on.html

Nils Hartmann

https://nilshartmann.net / @nilshartmann

Freiberuflicher Entwickler, Architekt, Trainer aus Hamburg

Java

JavaScript, TypeScript

React

Single-Page-Anwendungen

GraphQL

Inhalt

Das Beispiel

und der Beispiel-Workspace

Beispiel-Anwendung

Vorbereitung

git clone https://github.com/nilshartmann/react-training

cd react-training/blog-example/workspace

npm install

npm start

Aufrufen: http://localhost:3000

Slides: https://nils.buzz/techlab

Der Beispiel-Workspace

Im Verzeichnis blog-example/workspace ist schon alles vorbereitet

Du kannst dort mit npm install und npm start die (leere) Anwendung starten

In der Datei App.js kannst Du deine Anwendung implementieren
Dazu bitte in index.js die App-Komponente importieren (siehe Kommentare dort)

Wenn Du Änderungen machst, wird die Anwendung automatisch gebaut und im Browser aktualisiert

Tool-Chain starten

  1. cd code/blog-example/workspace
  2. npm start
  3. Öffne http://localhost:3000 im Browser
  4. Wenn auf der Seite 'Hello, World' steht, ist alles gut

Fertige Beispiele

In beispiele befinden sich mehrere Stände der fertigen Anwendung. Diese kannst Du zum Beispiel als Hilfe bei Problemen verwenden.

01_addpost-form: Nur das Formular zum Eingeben eines neuen Blog Posts. Du siehst hier die useState-Verwendung.

02_blog-app: Eingabeformular und darunter die Liste mit den Blog Posts, aber ohne Server-Calls. Du siehst hier, wie zwischen Komponenten kommuniziert wird (App und AddPostForm)

03_blog-app-mit-server: Genau wie 02, nur mit Server-Calls, dh Blog Posts werden vom Server gelesen und dort gespeichert

04_blog-app-mit-router: Ähnlich Stand 03, aber die einzelnen Komponenten sind über Links erreichbar. Außerdem gibt es eine Ansicht für einen einzelnen Blog-Post

Mögliche Features #1

Du kannst im Team eine App ganz nach deinen Wünschen bauen 😊

Zur Inspiration hier einige Ideen:

  • Schritt 1: Ein Formular zur Eingabe eines neuen Blog-Posts
  • Schritt 2: Neu eingegebene Blog-Posts werden in einer Liste unterhalb des Formulars angezeigt
    Architekur-Idee: Eigene Komponente für ein einzelnes Blog-Post, das Formular sowie eine zentrale App-Komponente die die Liste der Posts verwaltet
  • Schritt 3: Speichern der Blog-Posts auf dem Server
  • Schritt 4: Router

(Du siehst, dass entspricht "zufällig" auch den Ständen im beispiele-Verzeichnis 😇)

Mögliche Features #2

  • Validierung der Eingaben im Formular (keine Leer-Eingaben etc)
    Schwierigkeitsgrad: einfach
  • Das Formular kann ein- und ausgeblendet werden
    Schwierigkeitsgrad: mittel
  • Formular zur Eingabe des Blog-Posts auf einer einzelnen Seite
    Schwierigkeitsgrad: hoch
  • Darstellung eines einzelnen Posts auf einer eigenen Seite (Trennung in Übersichtsliste und Detail-Ansicht)
    Schwierigkeitsgrad: hoch

React Devtools

React Developer Tools für Chrome und Firefox

Untersuchen der React Anwendung zur Laufzeit

create-react-app

User Guide

Bootstrap von React Projekten

Fertige Konfiguration von Webpack, Bablel React, ...

Beispiel: npx create-react-app PROJEKTNAME

oder mit TypeScript Support: npx create-react-app PROJEKTNAME --typescript

React Grundlagen

React Dokumentation

https://reactjs.org/docs/hello-world.html

React Komponenten

  • bestehen aus Zustand, Logik und UI
  • keine Templatesprache
  • werden deklarativ beschrieben
  • werden immer komplett gerendert (kein 2-Wege-Data-Binding)
  • werden zu ganzen Anwendungen aggregiert

React Komponenten

  • Werden als Funktion mit Hooks oder ES6 Klasse implementiert
    • Hooks erst seit React 16.8 (Februar 2019)
    • Klassen (noch?) sehr weit verbreitet in bestehendem Code
  • Keine Templatesprache (stattdessen JavaScript)
    • Templates können HTML-artige Syntax enthalten (JSX)

Eine erste Komponente: Hello, World!

  • Beispiel Schritt-für-Schritt (blog-example/workspace)

Hello World React

HelloWorld.js

  import React from "react";
  
  export default function HelloWorld(props) {
    const [title, setTitle] = React.useState(props.initialTitle || "");
  
    return (
      <div>
        <input onChange={event => setTitle(event.target.value)} value={title} />
  
        <p>Eingegebener Titel: {greeting}</p>
        <button onClick={() => setTitle("")}>Clear</button>
      </div>
    );
  }
            

Aufruf

index.html


  <html>
    <-- ... -->
    <body>
      
</body> </html>

index.js


  import React from 'react';
  import ReactDOM from 'react-dom';
  
  import HelloWorld from './HelloWorld';
  
  ReactDOM.render(<HelloWorld initialTitle="Moin moin"/>, 
    document.getElementById('root')
  );
  

React: JSX

  • Wird wie HTML hingeschrieben, inkl Attribute:
    
    <div><input type="text"/></div>
                        
  • Achtung! class-Attribut heißt className:
    
                            <h1 className="title">...</h1>
                        
  • Attribute, die keine Strings sind, müssen in {} eingeschlossen werden:
    
    <Counter label="Count" count={7} showValues={true} />
                        
  • Kann pures JavaScript enthalten, eingeschlossen in {}:
    
    const title = 'Hello, World';
    <h1>{title.toUpperCase()}</h1>
                        
  • CSS-Eigenschaften werden als Objekt übergeben in Camel-Case-Notation:
    
    const styles = { marginLeft: '10px', border: '1px solid red' };
    <h1 style={styles}>...</h1>
                        

React: JSX #2

  • Fragmente (rendern selber kein Element in den DOM, nur ihre Kind-Elemente):
    
    function Choice() { 
      return <>
        <li>Yes</li> 
        <li>No</li>
      </>              
    }  
                        
  • null, false oder boolean, um nichts zu rendern:
    
    function ErrorMessage(props) {
      if (!props.msg) {
        return null; // oder false oder true
      }
    
      return 

    Fehler: {props.msg}

    ; }
  • Kommentare
    
      function MyComponent() {
        return 
    { /* hier ist javascript, deswegen block-kommentare erlaubt */ }
    ; }

React: Properties und Zustand

  • Properties werden der Komponente von außen übergeben (und nicht verändert)
  • Zustand (State) ist eine innere Eigenschaft der Komponente (die verändert werden kann)

Properties ("Props") einer Komponente

  • sind Objekte mit Key-Value-Paaren
  • werden als 1. Methoden-Parameter an Komponente übergeben
  • dürfen nicht verändert werden

            function Header(props) {
                return (
                  <h1 style={{color: props.titleColor}}>{props.title}</h1>
                );
              }
            }
            

// Mit Destructuring
function Header({titleColor, title}) {
    return (
      <h1 style={{color: titleColor}}>{title}</h1>
    );
  }
}
                

Zustand einer Komponente: useState-Hook

  • Beispiel: Inhalt eines Eingabefelds, Daten vom Server, Menu offen oder zu
  • Werte üblicherweise immutable
  • Arbeiten mit Zustand über useState-Hook
  • useState liefert Array mit zwei Werten zurück: aktuellen Zustand, und setter-Funktion um Zustand zu verändern

function HelloWorld(props) {
  const [title, setTitle] = React.useState(props.initialTitle);

  return <input onChange={e => setTitle(e.target.value) value={title} />;
}
                  
  • Aufruf des Setters löst erneutes rendern der gesamten Komponente aus
  • Es können mehrere States erzeugt werden, durch Verwendung mehrerer useState-Aufrufe
  • Zustand ist eines der zentralen Konzepte von React

React Hooks

Mit React Hooks kann sich eine Komponente in Zustand und Lebenszyklus "einhaken"
  • Hooks sind "normale" Funktionen, müssen aber mit use beginnen (useState, useEffect, ...)
  • Beispiel: Importieren und verwenden von Hooks
    
                    import React from "react";
    
                    function HelloWorld(props) {
                      const [title, setTitle] = React.useState(props.initialTitle);
                      // ...
                    }
                                      
    
                                          import React, { useState } from "react";
                          
                                          function HelloWorld(props) {
                                            const [title, setTitle] = useState(props.initialTitle);
                                            // ...
                                          }
                                                            
    • (React muss immer importiert werden, wenn JSX verwendet wird!)

React Hooks

Es gibt einige Regeln zu beachten, bei der Verwendung von Hooks 👆

(https://reactjs.org/docs/hooks-rules.html)

Einschränkungen:

  • Hooks können nur in Funktionskomponenten (und anderen Hooks) aufgerufen werden
  • Hooks müssen immer in derselben Reihenfolge und auf Top-Level-Ebene verwendet werden
    • Verboten z.B. in Schleifen, if-Abfragen oder in anderen Funktionen
  • Es gibt ein ESLint Plug-in zur korrekten Verwendung der Hooks

Der Hooks-Mechanismus basiert intern darauf, dass React sich die Reihenfolge der useXyz-Aufrufe merkt!

React Hooks

Beispiele für korrekte und unerlaubte Verwendung

              // ERLAUBT:
              function HelloWorld(props) {
                const [greeting, setGreeting] = React.useState(props.initialGreeting);
                const [name, setName] = React.useState(props.initialName);
                // ...
              }
                                

                                    // ERLAUBT:
                                    function HelloWorld(props) {
                                      const [greeting, setGreeting] = React.useState(props.initialGreeting);
                                      const uppercaseGreeting = greeting.toUpperCase(); 
                                      const [name, setName] = React.useState(props.initialName);
                                      // ...
                                    }
                                                      

                                    // VERBOTEN:
                                    function HelloWorld(props) {
                                      const [greeting, setGreeting] = React.useState(props.initialGreeting);
                                      if (greeting !== null) {
                                        const [name, setName] = React.useState(props.initialName);
                                      }
                                      // ...
                                    }
                                                      

                          // VERBOTEN:
                          function HelloWorld(props) {
                            const [greeting, setGreeting] = React.useState(props.initialGreeting);
                            if (greeting === null) {
                              return 

Please enter greeting first

; } // ... }

React Hooks

Beispiele für korrekte und unerlaubte Verwendung #2

              // VERBOTEN 
              function HelloWorld(props) {
                function initState() {
                  return React.useState(props.initialGreeting);
                }
                const [greeting, setGreeting] = initState();
              }
            

                // VERBOTEN (initState ist 'normale' Funktion)
                function initState() {
                  return React.useState(props.initialGreeting);
                }

                function HelloWorld(props) {
                  // wäre erlaubt, wenn initState 'useInitState' hieße
                  const [greeting, setGreeting] = initState();
                }
              

Render Zyklus

Virtual DOM

"Rendern" hat leider doppelte Bedeutung!

Listen

JSX bietet nichts für Listen

Ausgabe typischerweise über Array.map()

Elemente einer Liste brauchen einen eindeutigen Key


const posts = [
  { id: 0, title: 'Hello World', body: 'Lorem ipsum' },
  { id: 1, title: 'React in a Nutshell', body: 'Lets get started with React' }
];

function PostList(props) {
  return {props.posts.map(post => (
    <div key={post.id}>
        <h1>{post.title}</h1>
        <p>{post.body}</p>
    </div>
  )}
}

Komponentenhierarchien

Datenfluss in React-Anwendungen

  1. In React-Anwendungen werden Komponenten zu Anwendungen in Hierarchien zusammengesteckt
  2. Innerhalb einer Hierarchie wird immer nur in eine Richtung kommuniziert: Eltern-Komponenten übergeben Properties an ihre Kinder
  3. Mit den Properties werden Daten von "oben" nach "unten" gereicht (ggf. über mehr als eine Hierarchie-Ebene)
  4. Über Properties können auch Callback-Funktionen nach unten gereicht werden.
  5. Eine Kind-Komponente kann diese Funktion dann aufrufen, und der Eltern Komponente darüber ein Signal geben
    (so, wie wir das schon beim onChange-Property des input-Felds gesehen haben)

Smart und Dumb-Komponenten #1

Zur Erinnerung: in React bauen wir Komponenten. Komponenten bestehen aus Logik, Zustand und UI (HTML-Elemente und Styling)

Ein bekanntes Muster ist, die Komponenten in zwei Arten aufzuteilen: Smart (oder Controller)- und Dumb oder (Presentation-)-Komponenten

Technisch sind die Komponenten identisch, also "normale" React-Komponenten

Nur ihre Aufgabe ist anders definiert...

Smart und Dumb-Komponenten #2

Smart-Komponenten enthalten Logik und Zustand

Dumb-Komponenten sind nur zur Darstellung der Daten

Smart-Komponenten reichen Zustand in die Dumb-Komponenten. Diese zeigen den Zustand an

Smart-Komponenten reichen Callback-Funktion als Event-Handler an die Dumb-Komponenten

Wenn in Dumb-Komponenten ein Ereignis eintritt (z.B. Button-Click oder Texteingabe), wird eine Callback-Funktion aufgerufen

Die Callback-Funktion wird dann in der Smart-Komponente aufgerufen und die Verarbeitung ausgeführt

Die Smart-Komponente setzt ihren Zustand neu, und rendert sich und ihre Kinder (die Dumb-Komponenten) neu

Beispiel

Unsere Smart-Komponente hält eine Liste von Blog-Posts und steuert, welche Ansicht aktiv ist

Die Smart-Komponente gibt die Liste der Blog-Posts an die BlogList zum Anzeigen

Die Smart-Komponente gibt jeweils eine Callback-Funktion an die BlogList und die AddForm


function App() {
  const [posts, setPosts] = React.useState([]);
  const [view, setView] = React.useState("list");

  function addPost(newPost) {
    // Neuen Post hinzufügen
    setPosts([...posts, newPost]);

    // Wieder Liste anzeigen
    setView("list");
  }

  if (view === "list") {
    return <BlogList posts={posts} onAdd={() => setView("addForm")} />
  }

  return <AddForm onAdd={addPost} />

}            

Beispiel #2

Die BlogList zeigt die übergebene Liste nur an und informiert die App, wenn auf den "Add"-Button gedrückt wurde

Die App kann dann die andere Komponente (AddForm) anzeigen


  function BlogList(props) {

    return <div>
      // ... Liste anzeigen ...
      <button onClick={props.onAdd}>Add Blog Post</button>
    </div>;
  }            
  

Beispiel #3

Die AddForm erfasst einen neuen BlogPost und übergibt diesen der Callback-Funktion, so dass die App-Komponente ihn in die Liste der BlogPosts (State) einfügen kann


  function AddForm(props) {
    const [title, setTitle] = React.useState("");
    const [body, setBody] = React.useState("");

    function addPost() {
      const newPost = {
        title, body
      }

      // App-Komponente informieren
      props.onAdd(newPost);
    }

    return <div>
      // ... Formular rendern ...
      <button onClick={addPost}>Save Post</button>
    </div>;
  }            
  

Daten lesen und schreiben vom Server

Der Blog-Server

Der Server ist bereits fertig. Zum Starten:


                  cd react-training/blog-example/backend
                  npm start
                

Der Server ist über Port 7000 erreichbar

Zum Testen: http://localhost:7000/posts

Der Blog-Server

Die Endpunkte:

GET /posts Alle Blog Posts abfragen

GET /posts?short: Alle Blog Post IDs und deren Titel

GET /posts/:id Einen einzelnen Blog Post lesen

POST /posts Einen neuen Blog Post anlegen. Als Payload/Body muss ein Objekt mit title und string übertragen werden. Als Ergebnis wird der komplette, neue Blog Post (inklusive ID) zurück gegeben

DELETE /posts/:id Einen Blog-Post löschen

Außerdem kann allen Requests der URL Parameter?slow übergeben werden, um die Antwort künstlich zu verlangsamen (wenn ihr z.B. mit Wartezuständen arbeiten wollt)

Server-Calls

React macht keine Angabe, wie Server-Calls (technisch) gemacht werden

Häufig in React verwendet: fetch API

Beispiel: fetch

Daten lesen per GET


          // Für GET Zugriff reicht es, die URL anzugeben:
          try {
            const response = await fetch('http://localhost:7000/api/greetings')
            const json = await response.json();
            // ...
          } (catch ex) {
            console.error('request failed', ex)
          }
          

// Alternative mit Promise:

fetch('http://localhost:7000/posts')
  .then(response => response.json())
  .then(json => /* ... */)
  .catch(ex => console.error('request failed', ex));

Beispiel #2: fetch

Daten lesen per POST

fetch erwartet als zweiten Parameter ein Objekt mit Konfigurationsparametern, u.a:

  • method: gibt die HTTP Methode an ( PUT, POST, DELETE, ...)
  • headers: Objekt mit HTTP Headern für den Request
  • body: Der Request-Payload (als String)

const response = await fetch(url, {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(payload)
})
// ... 
    

fetch im Detail


  try {
    // 1. fetch gibt ein Promise zurück, dass mit dem
    // Response-Objekt aufgelöst wird, wenn die Antwort vom Server
    // kommt
    const response = await fetch('http://localhost:7000/posts');
  
    // 2. das Response Objekt enthält eine json() Funktion,
    // die das geparse JSON aus der Antwort zurückliefert
    const posts = await response.json();
  
    // WAS MACHEN WIR MIT DER ANTWORT?
    // ???
  } catch (err) {
    // 4. Falls etwas schief geht, Fehler loggen
    console.error('request failed', err);
  }
  
  

Fetch API Doku

useEffect-Hook

In der Render-Phase einer Komponente (das ist die Ausführung der Komponenten-Funktion) darfst Du keine Seiteneffekte (z.B. Server-Zugriffe) machen.

Der useEffekt-Hook erlaubt es dir aber, Seiteneffekte zu definierten (späteren) Zeitpunkten auszuführen

useEffect-Hook

Als 1. Parameter übergibst Du eine Callback-Funktion, die deinen Code enthält. Dieser darf Seiteneffekte enthalten, z.B. einen fetch-Aufruf


                function App(props) {
                  React.useEffect( 
                    () => console.log("Ich werde nach JEDEM Rendern ausgeführt")
                  );
                }
              
2. Parameter (Array) gibt an, wann der Effekt ausgeführt werden soll:

                function App(props) {
                  React.useEffect( 
                    () => console.log("Ich werde nur nach 1. Rendern ausgeführt"),
                    []
                  );
                }
              
Nach 1. Rendern ausführen und wenn sich das postId-Property ändert:

                  function BlogPost(props) {
                    React.useEffect(
                      () => console.log("..."), 
                      [props.postId]
                    );
                  }
                

useEffect Hook

Zwei Parameter:

  1. Callback-Funktion, die aufgerufen wenn entsprechendes Ereignis eintritt (z.B. initiales Rendern abgeschlossen)
  2. Ein Array mit Abhängigkeiten:
    • Wenn kein Array angegeben wird, wird der Effekt nach jedem Rendern ausgeführt (Achtung! Endlosschleife möglich)
    • Wenn ein leeres Array angegeben wird, wird der Effekt nur nach dem 1. Rendern ausgeführt
    • Wenn Werte angegeben werden, wird der Effekt ausgeführt, wenn sich mind. 1 Wert zwischen zwei Render-Zyklen verändert hat

Initiales Laden von Daten

useEffect und (useState) werden verwendet um Daten nach dem 1. Rendern zu laden:


                function App() {
                  const [posts, setPosts] = React.useState([]);
                
                  React.useEffect(() => {
                    fetch("http://localhost:7000/posts")
                      .then(response => response.json())
                      .then(json => setPosts(json));
                  }, []);

                  return {posts.map(p => (
                    <Post key={p.id} post={p} />
                  ))}
                }
                
                

Initiales Laden von Daten

Lifecycle des gezeigten Beispiels:

  1. Komponenten-Funktion wird ausgeführt (Komponente wird "gerendert")
  2. State mit leerem Array initialisiert
  3. Effekt zum Laden der Daten wird registriert
  4. UI-Code wird zurückgegeben (leere Liste mit Posts, da noch keine Daten geladen sind)
  5. React erzeugt den nativen DOM für die UI
  6. Der registrierte Effekt wird ausgeführt, der Server-Call gestartet
  7. Der Server-Call kommt zurück, die Daten werden in den State gesetzt
  8. Durch das Setzen des States, wird die Komponente erneut gerendert, also wird die Komponenten-Funktion neu aufgerufen
  9. Der State ist nun das Array mit den geladenen Daten
  10. Der Effekt wird nicht erneut registriert (leeres Array als 2. Parameter)
  11. Die Komponente liefert nun die UI für die geladene Liste zurück

Speichern von Daten

Zum Beispiel als Folge einer Benutzerinteraktion:

Im Event-Handler dürfen wir direkt Seiteneffekte nutzen


function App(props) {
  // laden, wie gesehen
  React.useEffect( ... );

  function addPost(post) {
    fetch("http://localhost:7000/posts", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(post)
    })
      .then(response => response.json())
      .then(newPost => setPosts([newPost, ...posts]));
  }


  return
    ...
      <CreatePostForm onAdd={newPost => addPost(newPost)} />
    ...
}
                

Client-seitiges Routing

Beispiel: code/blog-example/beispiele/04_blog-app-mit-router

Warum Routing?

Mappen von URLs auf Komponenten
(Navigation findet ohne Server-Roundtrip statt)

Komponenten halten (Teil) des Zustandes der Anwendung
Welche "Seite" ist sichtbar (Blog Liste, Formular oder Einzelansicht)?
Welche Daten werden dafür geladen (z.B. Blog Post Id)

React Router

  • Aktuelle Version 5.1: https://reacttraining.com/react-router/
  • Kein Bestandteil von React
  • Sehr häufig verwendet (de-facto Standard)
  • (Erst) seit Version 5.1 mit Hooks API (useParams, useLocation, useHistory)

Das Router-Objekt

Top-Level-Objekt, das einmalig (oben) in der Komponenten Hierarchie eingebunden werden muss

Mehrere Ausprägungen zum Arbeiten mit den URL und der Browser History:

  • HashRouter: codiert Pfad in angehängten Hash (#/post/1)
  • BrowserRouter: codiert Pfad direkt in URL (/post/1)

      import {HashRouter as Router} from "react-router-dom";
      
      const app = <Router><App/></Router>;
      
      ReactDOM.render(app, document.getElementById(...));
              

Welche Komponente soll für einen Pfad gerendert werden

Das Route-Objekt mappt Pfade auf Komponenten

  • Wird verwendet, wo Pfad-abhängig Komponenten ausgewählt werden sollen
    • vergleichbar mit intelligentem if/ switch statement
    • Kann überall in der Anwendung verwendet werden (auf allen Hierarchie-Ebenenen)
  • Mit path wird der Pfad übergeben, für den die Route matchen soll
  • Als Children wird die Komponente angegeben

      import {HashRouter as Router, Route} from "react-router-dom";
      
      const app = <Router>
        <Route path="/post/:postId"><BlogPostPage /></Route>
        <Route path="/add"><AddPostPage onAdd={...} /></Route>
        <Route path="/" exact><BlogListPage /></Route>
      </Router>;
      
      ReactDOM.render(app, document.getElementById(...));
              

Hintergrund: Pfade

In Routen werden Pfade angegeben, die mit der aktuellen URL verglichen werden

  • Pfade sind per Default gültig für Teilstrings, aber mit exact kann das Verhalten verändert werden
  • Eine Route ohne path matcht immer

      // trifft zu für / und /greeting
      <Route path="/">...</Route>
      
      // trifft nur zu für /
      <Route path="/" exact>...</Route>
      
      // passt auf JEDE URL:
      <Route>...</Route>
      
              
  • Kann variable Segmente enthalten:

      <Route path="/posts/:postId"><BlogPostPage/></Route>
            
  • Werte der variablen Parameter werden mit useParams in der Komponente abgefragt:

                import { useParams } from "react-router";

                function BlogPostPage() {
                  const params = useParams();

                  // params.postId enthält den variablen Wert aus der URL
                

Switch

  • Wenn mehrere path-Ausdrücke matchen, werden mehrere Komponenten gerendert (z.B. "/" und "/post")
  • Switch sorgt dafür, dass nur die erste Komponente im Block gerendert wird

      import {HashRouter as Router, Route, Switch} from "react-router-dom";
      
      const app = (
        <Router>
          <Switch>
            <Route path="/post/:postId"><BlogPostPage /></Route>
            <Route path="/" exact><BlogListPage/></Route>
      
            // "No match": ohne Pfad
            <Route><NotFoundPage/></Route>
      
          </Switch>
        </Router>
      );
      
      ReactDOM.render(app, document.getElementById(...));
              

Links

Mit Link und NavLink können Links erzeugt werden

  • Mit to wird das Ziel angegeben
  • Gerendert wird per default ein a Element
  • URL wird entsprechend der History (Browser oder Hash) erzeugt
  • Mit activeClassName und activeStyle auf NavLink können Styles übergeben werden, die angewendet werden, wenn der Link der aktiven Route entspricht

      import {Link, NavLink} from "react-router-dom";
      
      <Link to='/'>Show all Posts</Link>
      
      // Erzeugtes 'a' Element erhält 'highlight' CSS-Klasse, wenn die aktive Route
      <NavLink to='/add' activeClassName="highlight">Add Post</NavLink>
      
              

Das history-Objekt

Mit dem history-Objekt kann mit der Browser History interagiert werden

Mit der History kann auf andere URLs gesprungen werden oder die Location abgefragt werden

Komponenten bekommen das history-Objekt über den useHistory Hook


      import { useHistory } from "react-router";

      function App() {
        const history = useHistory();

        function onAdd(newBlogPost) {
           ...

           // gehe zu neuer URL
           history.push("/"); 

           // Alternativ: gehe zu neuer URL, lösche aber aktuelle aus 
           // History im Browser
           history.replace("/...") 
        }

        return ...;
      }
              

Geschafft 😊

Vielen Dank für Eure Teilnahme!

Kontakt:

Mail: nils@nilshartmann.net

Web: https://nilshartmann.net

Twitter: @nilshartmann