Slide 1

Slide 1 text

React 2018 NILS HARTMANN W-JAX MÜNCHEN | NOVEMBER 2018 | @NILSHARTMANN Slides: https://bit.ly/wjax2018-react Context API, Suspense, Time Slicing & mehr

Slide 2

Slide 2 text

@NILSHARTMANN NILS HARTMANN Programmierer aus Hamburg JavaScript, TypeScript, React Java Trainings, Workshops [email protected] !

Slide 3

Slide 3 text

ALLES NEU? Fiber React 16

Slide 4

Slide 4 text

REACT 2018

Slide 5

Slide 5 text

ZUM VERGLEICH...

Slide 6

Slide 6 text

ZUM VERGLEICH...

Slide 7

Slide 7 text

EIN BEISPIEL... https://github.com/nilshartmann/react-chat-example

Slide 8

Slide 8 text

GLOBALE DATEN IN DER ANWENDUNG Context 16.3

Slide 9

Slide 9 text

REACT CONTEXT • Problem: Globale Daten in der Anwendung

Slide 10

Slide 10 text

REACT CONTEXT • Problem: Globale Aktionen in der Anwendung

Slide 11

Slide 11 text

REACT CONTEXT Globale Daten: Mit Properties (Durchreichen)

Slide 12

Slide 12 text

REACT CONTEXT Globale Daten: Mit externem State Management (Redux/Mobx)

Slide 13

Slide 13 text

REACT CONTEXT Globale Daten: Mittels Context API • Provider bietet Daten an • Consumer kann auf Daten zugreifen

Slide 14

Slide 14 text

REACT CONTEXT VERWENDEN Context Erzeugen // Context erzeugen: const UserContext = React.createContext({ user: null }); // Provider-Komponente: UserContext.Provider; // Consumer-Komponente: UserContext.Consumer;

Slide 15

Slide 15 text

REACT CONTEXT API React Context: Verwenden des Providers • Daten werden mit "value"-Property angegeben • Daten können dann in Unterkomponenten konsumiert werden class UserDataManager extends React.Component { render() { const value = { } return {this.props.children} } }

Slide 16

Slide 16 text

REACT CONTEXT API React Context: value-Objekt • Stellt die eigentlichen Daten zur Verfügung class UserDataManager extends React.Component { render() { const value = { user: this.state.user, } return {this.props.children} } }

Slide 17

Slide 17 text

REACT CONTEXT API React Context: value-Objekt • Stellt die eigentlichen Daten zur Verfügung • Callback führt zu State-Änderung, Provider wird neu gerendert, Konsumenten erhalten neue Werte (analog zu Properties) class UserDataManager extends React.Component { render() { const value = { user: this.state.user, login = userId => { const newUser = loginViaApi(userId); this.setState({user: newUser}) } } return {this.props.children} } }

Slide 18

Slide 18 text

REACT CONTEXT API import UserContext from "..."; class UserProfile extends React.Component { render() { return { values => { // ... work with data from context ... } } } } Consumer: Daten aus Context verwenden Function as Children

Slide 19

Slide 19 text

HINTERGRUND: RENDER PROPS Render Properties • Über ein Property wird einer Komponente ein Callback übergeben • Dieses Callback liefert aber keine Daten, sondern rendert eine Komponente • Populär z.B. in React Router und Apollo GraphQL

Slide 20

Slide 20 text

HINTERGRUND: RENDER PROPS Render Properties class ChatMessageView extends React.Component { render() { return <>

Latest Chat Message

Please wait, Data is loading

} onMsg={ (msg) =>

{msg.title} {msg.body}

} /> >; } }

Slide 21

Slide 21 text

HINTERGRUND: RENDER PROPS Speziallfall: Function as Children • Callback wird als Children übergeben class ChatMessageView extends React.Component { render() { return <>

Latest Chat Message

{ (load, msg) => { if (load) {

Please wait, Data is loading

} return

{msg.title} {msg.body}

} } /> >; } } Function as Children

Slide 22

Slide 22 text

REACT CONTEXT API import ChatContext from "..."; class UserProfile extends React.Component { render() { return { values => { } } } } Consumer: Daten aus Context verwenden

Slide 23

Slide 23 text

REACT CONTEXT API import ChatContext from "..."; class UserProfile extends React.Component { render() { return { values => { if (values.user) { return

Hello, {values.user}

} return values.login()}>Login } } } } Consumer: Daten aus Context verwenden "Action" auslösen Auf Daten zugreifen

Slide 24

Slide 24 text

REACT CONTEXT API import UserContext from "..."; import ChatContext from "..."; class Chatroom extends React.Component { render() { return { user => { { chat => { return

Hello {user.name}, you're in {chat.room}

} } } } } } Consumer: Mehrere Kontexte verwenden • Kontexte können z.B. fachlich aufgeteilt werden

Slide 25

Slide 25 text

REACT CONTEXT API • Consumer: contextType [16.6] • Alternativer Zugriff (nur in Klassen, nur bei einem Kontexttype) import ChatContext from "..."; class UserProfile extends React.Component { static contextType = ChatContext; render() { if (this.context.user) { return

Hello, {this.context.user}

} return this.context.login()}>Login } }

Slide 26

Slide 26 text

RENDERN UNTERBRECHEN Suspense 16.6

Slide 27

Slide 27 text

SUSPENSE Suspense: React kann das Rendern von Komponenten unterbrechen, während (asynchron) Daten geladen werden [16.6] • Funktioniert aktuell (nur) für Code Splitting

Slide 28

Slide 28 text

DEMO: LAZY UND SUSPENSE • Demo: Fallback Komponente http://localhost:9080/?delay

Slide 29

Slide 29 text

DEMO: LAZY UND SUSPENSE • Netzwerk Requests http://localhost:9080/?delay

Slide 30

Slide 30 text

SUSPENSE React.lazy: Code splitting with Suspense [16.6] const ChatPage = React.lazy(() => import("./chat/ChatPage")); class App { render() { return <> // more pages... <> } } Dynamic Import

Slide 31

Slide 31 text

SUSPENSE React.Suspense: Zeigt Fallback Komponente an [16.6] • Bis Komponente geladen ist, muss Spinner o.ä. angezeigt werden const ChatPage = React.lazy(() => import("./chat/ChatPage")); class App { render() { return <> Loading...}> // more pages... <> } }

Slide 32

Slide 32 text

DEMO: LAZY UND SUSPENSE Problem: "Flickern" • Entsteht, wenn Ladezeiten eher schnell sind

Slide 33

Slide 33 text

AUSBLICK Concurrent React 16.7-alpha unstable!

Slide 34

Slide 34 text

SUSPENSE MIT CONCURRENT MODE Suspense: Flickern verhindern mit Concurrent Mode • maxDuration legt eine Zeit fest, bis fallback gerendert wird • Bis dahin wird bestehende Komponente angezeigt • Vor React 16.7 nicht / nur schwer möglich const ChatPage = React.lazy(() => import("./chat/ChatPage")); class App { render() { return <> ...}> // more pages... <> } }

Slide 35

Slide 35 text

LAZY UND SUSPENSE • Demo: Fallback Komponente mit maxDuration [16.7-alpha] http://localhost:9081

Slide 36

Slide 36 text

SUSPENSE Ausblick [16.7]: Weitere Anwendungsfälle • Alle gezeigten Beispiele verwenden unstable API!! unstable api!

Slide 37

Slide 37 text

BEISPIEL: DATEN LADEN MIT SUSPENSE • REST Aufrufe mit fetch /api/logs /api/users unstable api!

Slide 38

Slide 38 text

ASYNCHRONES DATEN LADEN • "Klassisches" Daten laden • In componentDidMount Daten das Laden anstoßen • In der Zwischenzeit Loading Indicator anzeigen class LogsView extends React.Component { state = {}; async componentDidMount() { const response = await fetch("/api/logs"); const logs = await response.json(); this.setState({ logs: logs }) } render() { if (!this.state.logs) { return

Loading...

} return <> // render logs >; } } unstable api!

Slide 39

Slide 39 text

DATEN LADEN MIT SUSPENSE - 1 • Daten laden mit Suspense • Vor dem Rendern wird Funktion aufgerufen die Daten liefert – oder auch nicht • Sobald die Funktion (später) Daten liefert, wird die Komponente gerendert function LogsView() { const logs = LogsResource.read(); // kehrt nur mit Daten zurück return <> ...geladene logs hier anzeigen... >; } unstable api!

Slide 40

Slide 40 text

DATEN LADEN MIT SUSPENSE - 2 • Daten laden mit Suspense • Vor dem Rendern wird Funktion aufgerufen die Daten liefert – oder auch nicht • Sobald die Funktion (später) Daten liefert, wird die Komponente gerendert • Komponente wird irgendwo im Tree mit Suspense umschlossen function LogsView() { const logs = LogsResource.read(); // kehrt nur mit Daten zurück return <> ...geladene Logs hier anzeigen... >; } function DashboardPage() { return } unstable api!

Slide 41

Slide 41 text

DATEN LADEN MIT SUSPENSE - 3 • react-cache (zzt 2.0.0-alpha): Noch experimentell! • Geladene Daten (Resourcen) können gecached werden • Wenn Daten noch nicht vorhanden, werden sie vom Server gelesen import { unstable_createResource } from "react-cache"; // Liefert Promise zurück async function loadLogsFromApi() { const response = await fetch("http://localhost:9000/api/logs"); return await response.json(); } const LogsResource = unstable_createResource(loadLogsFromApi); unstable api!

Slide 42

Slide 42 text

DATEN LADEN MIT SUSPENSE • Demo: Suspense an diversen Stellen http://localhost:9081/dashboard?delayfetch unstable api!

Slide 43

Slide 43 text

HINTERGRUND: SUSPENSE • Wie funktioniert das eigentlich? function LogsView(props) { const logs = LogsResource.read(); // ! wird nur ausgeführt, wenn logs zurückgeliefert wird: "" return <> ... Logs hier anzeigen ... >; } unstable api!

Slide 44

Slide 44 text

BEISPIEL: VORSCHAUEN MIT RESPONSE • Suspense nutzen, um Vorschauen zu laden Demo: http://localhost:9081/?delayimg unstable api!

Slide 45

Slide 45 text

BEISPIEL: VORSCHAUEN MIT SUSPENSE Vorher – ohne Vorschau, so wie gewohnt function Avatar(props) { const src = `/avatars/${props.userId}.svg`; return ; } function ChatMessage(props) { return (
{ props.message.text} ...
); }

Slide 46

Slide 46 text

BEISPIEL: VORSCHAUEN MIT SUSPENSE Avatar Komponente mit Suspense function Avatar(props) { const src = `/avatars/${props.userId}.svg`; ImageResource.read(src); // <-- "Wartet" auf Image return ; } credits: @jaredpalmer https://github.com/jaredpalmer/react-conf-2018/blob/master/full-suspense/src/components/ArtistDetails.js unstable api!

Slide 47

Slide 47 text

BEISPIEL: VORSCHAUEN MIT SUSPENSE Image Resource function Avatar(props) { const src = `/avatars/${props.userId}.svg`; ImageResource.read(src); // <-- "Wartet" auf Image return ; } const ImageResource = unstable_createResource( source => new Promise(resolve => { const img = new Image(); img.src = source; img.onload = resolve; } ) ) credits: @jaredpalmer https://github.com/jaredpalmer/react-conf-2018/blob/master/full-suspense/src/components/ArtistDetails.js unstable api! "Trick", um zu warten, bis der Browser ein Image geladen hat

Slide 48

Slide 48 text

BEISPIEL: VORSCHAUEN MIT SUSPENSE Einbinden function ChatMessage(...) { return (
}> { props.message.text} ...
); } unstable api!

Slide 49

Slide 49 text

FUNCTIONS EVERYWHERE Hooks 16.7-alpha

Slide 50

Slide 50 text

FUNCTIONS EVERYWHERE Hooks 16.7-alpha h"ps://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889

Slide 51

Slide 51 text

FUNCTIONS EVERYWHERE Hooks 16.7-alpha Unstable! Experimental! Proposal!

Slide 52

Slide 52 text

FUNCTIONS EVERYWHERE Hooks 16.7-alpha Official Documentation https://reactjs.org/docs/hooks-intro.html

Slide 53

Slide 53 text

HINTERGRUND Hooks: State, Context etc auch in Funktionskomponenten Motivation: • Bessere Wiederverwendbarkeit von Code • Logik in Klassen nicht immer einfach verständlich (insb Lifecycles) Hooks sind reguläre Funktionen

Slide 54

Slide 54 text

USESTATE HOOK useState: State in Funktionskomponenten Beispiel: Tab Bar function Tabs(props) { return
...
} unstable api!

Slide 55

Slide 55 text

USESTATE HOOK useState: State erzeugen function Tabs(props) { const [activeTabId, setActiveTabId] = React.useState(0); } Default Wert Setter Aktueller State unstable api!

Slide 56

Slide 56 text

USESTATE HOOK useState: Aktuellen State verwenden function Tabs(props) { const [activeTabId, setActiveTabId] = React.useState(0); return (
{props.tabs.map(tab => { return })}
); } Zugreifen auf State unstable api!

Slide 57

Slide 57 text

USESTATE HOOK useState: State verändern function Tabs(props) { const [activeTabId, setActiveTabId] = React.useState(0); return (
{props.tabs.map(tab => { return setActiveTabId(tab.id)} /> })}
); } Setzen von State (kein Objekt mehr!) unstable api!

Slide 58

Slide 58 text

USESTATE HOOK useState: Mehrere States in einer Komponente möglich • Kein "mergen" von State mehr! function LoginForm(props) { const [username, setUsername] = React.useState("klaus"); const [password, setPassword] = React.useState(""); return (<> setUsername(e.target.value)} /> setPassword(e.target.value)} /> >); } unstable api!

Slide 59

Slide 59 text

ARBEITEN MIT SEITENEFFEKTEN Server-Zugriffe, Subscriptions etc sind Seiteneffekte unstable api!

Slide 60

Slide 60 text

ARBEITEN MIT SEITENEFFEKTEN Server-Zugriffe, Subscriptions etc sind Seiteneffekte • Bislang nur in Klassen-Komponenten class ChatPage extends React.Component { componentDidMount() { this.disconnectFromApi = ChatApi.subscribe(this.props.apiKey); } render() { return

Chat

...
} } unstable api!

Slide 61

Slide 61 text

ARBEITEN MIT SEITENEFFEKTEN Server-Zugriffe, Subscriptions etc sind Seiteneffekte • Bislang nur in Klassen-Komponenten class ChatPage extends React.Component { componentDidMount() { this.disconnectFromApi = ChatApi.subscribe(this.props.apiKey); } componentWillUnmount() { this.disconnectFromApi() } render() { return

Chat

...
} } unstable api!

Slide 62

Slide 62 text

ARBEITEN MIT SEITENEFFEKTEN Server-Zugriffe, Subscriptions etc sind Seiteneffekte • Bislang nur in Klassen-Komponenten class ChatPage extends React.Component { componentDidMount() { this.disconnectFromApi = ChatApi.subscribe(this.props.apiKey); } componentWillUnmount() { this.disconnectFromApi() } componentDidUpdate(prevProps) { if (prevProps.apiKey !== this.props.apiKey) { ChatApi.subscribe(this.props.apiKey); } } render() { return

Chat

...
} } Nur ausführen, wenn Properties sich geändert haben unstable api!

Slide 63

Slide 63 text

ARBEITEN MIT SEITENEFFEKTEN useEffect: Seiteneffekte in Funktionskomponenten function ChatPage(props) { React.useEffect( () => { const disconnectFromApi = ChatApi.subscribe(props.apiKey); }, ); return

Chat

...
} Ersetzt componentDidMount & componentDidUpdate unstable api!

Slide 64

Slide 64 text

ARBEITEN MIT SEITENEFFEKTEN useEffect: Seiteneffekte in Funktionskomponenten Aufräumen in Rückgabe-Funktion function ChatPage(props) { React.useEffect( () => { const disconnectFromApi = ChatApi.subscribe(props.apiKey); return () => disconnectFromApi(); }, ); return

Chat

...
} Ersetzt componentWillUnmount unstable api!

Slide 65

Slide 65 text

ARBEITEN MIT SEITENEFFEKTEN useEffect: Seiteneffekte in Funktionskomponenten Bedingte Ausführung function ChatPage(props) { React.useEffect( () => { const disconnectFromApi = ChatApi.subscribe(props.apiKey); return () => disconnectFromApi(); }, [props.apiKey] ); return

Chat

...
} Ersetzt Property-Vergleich in componentDidUpdate unstable api!

Slide 66

Slide 66 text

WEITERE HOOKS (Fast) alles geht jetzt mit Hook • useState • useEffect • useContext • useRef • useReducer • (Noch offen: Error Boundaries) unstable api!

Slide 67

Slide 67 text

CUSTOM HOOKS Eigene Hooks sind möglich und können wiederverwendet werden • Beispiel: Handler für Input-Felder unstable api!

Slide 68

Slide 68 text

CUSTOM HOOKS Eigene Hooks sind möglich und können wiederverwendet werden • Beispiel: Handler für Input-Felder function useFormInput(initialValue, onEnter) { const [value, setValue] = React.useState(initialValue); function onEnterHandler(e) { const keyCode = e.which || e.keyCode; if (keyCode === 13) { onEnter(value); } } return { value, onChange: e => setValue(e.target.value), onKeyPress: onEnterHandler }; } unstable api!

Slide 69

Slide 69 text

CUSTOM HOOKS Eigene Hooks sind möglich und können wiederverwendet werden • Beispiel: Handler für Input-Felder function useFormInput(initialValue, onEnter) { ... } // Verwendung: function LoginDialog(props) { const usernameInput = useFormInput("", ChatApi.login); return } unstable api!

Slide 70

Slide 70 text

CUSTOM HOOKS Eigene Hooks sind möglich und können wiederverwendet werden • Beispiel: Generischer "fetch hook" • Alle Hooks können verwendet werden function useApi(path, initialData) { const [data, setData] = React.useState(initialData); React.useEffect(async () => { const response = await fetch(`http://localhost:9000/${path}`); const data = await response.json(); setData(data); }, [path]); return data; } unstable api!

Slide 71

Slide 71 text

CUSTOM HOOKS Eigene Hooks sind möglich und können wiederverwendet werden • Beispiel: Generischer "fetch hook" function useApi(path, initialData) { ... } // Verwendung function Dashboard(props) { const logs = useApi("/logs", []); const users = useApi("/users", []); return <> >; } unstable api!

Slide 72

Slide 72 text

HOOKS • Müssen wir jetzt alle Hooks verwenden? ! • Was ist mit unseren Klassen? ! unstable api!

Slide 73

Slide 73 text

HOOKS • Müssen wir jetzt alle Hooks verwenden? ! • Was ist mit unseren Klassen? ! • Zunächst: • Hooks sind "opt-in" • Hooks sind abwärtskompatibel • Eingeführt in Minor-Version (!) unstable api!

Slide 74

Slide 74 text

HOOKS • Müssen wir jetzt alle Hooks verwenden? ! • Was ist mit unseren Klassen? ! • ...also: keine Panik! React bleibt stabil! ☺ https://reactjs.org/docs/hooks-intro.html#gradual-adoption-strategy unstable api!

Slide 75

Slide 75 text

Vielen Dank! @NILSHARTMANN [email protected] Slides: https://bit.ly/wjax2018-react Beispiel-Code: http://bit.ly/wjax2018-react-example