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