JSX

Themen

  • Binden von Inhalten und Properties
  • Whitespace, Kommentare, Escapes und Fragmente
  • if / else
  • Wiederholen von Elementen
  • Styling-Grundlagen
  • Binden von Events
  • JSX und Sicherheit
  • Kompilierung

JSX

JSX = Templatesprache von React

  • ein XML-Tag wechselt von JS zu XML/HTML
  • geschweifte Klammern wechseln zurück zu JS

Gültige Elemente in JSX

  • string
  • number
  • Komponenten (z.B. <div>, <img>, <MyComponent>)
  • Arrays anderer Elemente
  • null, undefined, true, false (werden nicht gerendert)

JSX: Binden von Inhalten und Properties

Inhalte binden

wir können Zahlen und Strings als grundlegende Typen direkt einbinden:

const dateString = new Date().toLocaleDateString();
<div>curent date: {dateString}</div>

Properties binden

Der Wechsel von XML auf JS klappt auch bei Properties:

<a href={'https://en.wikipedia.org/wiki/' + articleName}>
  wikipedia article
</a>

Beachte die fehlenden Anführungszeichen bei href

Properties binden

Steht eine Property in Anführungszeichen, haben die geschweiften Klammern keine besondere Bedeutung:

<a href="https://google.com/search?q=what+is+${}+in+js">
  google search
</a>

Properties binden

setzen von booleschen HTML-Properties:

<button disabled>disabled button</button>

oder

<button disabled={true}>disabled button</button>

Property-Namen

Manche Properties von Elementen haben andere Namen als in standard HTML (spiegeln standard DOM-Properties wider)

  • className (anstatt class)
  • htmlFor (anstatt for)

JSX: Whitespace, Kommentare und Fragmente

Whitespace

in HTML sind die folgenden Beispiele äquivalent (und enthalten je ein Leerzeichen zwischen den Bildern):

<img src="foo.png" /> <img src="bar.png" />
<img src="foo.png" />    <img src="bar.png" />
<img src="foo.png" />
<img src="bar.png" />

Whitespace

in JSX gilt:

  • Whitespace zwischen Elementen innerhalb einer Zeile wird als einzelnes Leerzeichen interpretiert
  • Whitespace zwischen Elementen, der über mehrere Zeilen geht, wird ignoriert

Einzelnes Leerzeichen:

<img src="foo.png" />     <img src="bar.png" />

Kein Leerzeichen:

<img src="foo.png" />
<img src="bar.png" />

Whitespace

"Erzwungenes" Einfügen eines Leerzeichens in JSX:

<p>
  long multi-line text where spaces{' '}
  <strong>are wanted</strong>
</p>

Escapes

Sonderzeichen wie < können mittels JavaScript eingefügt werden:

<p>
  if a {'<'} max {'&'} a {'>'} min then ...
</p>

Kommentare

Kommentare können als JavaScript-Kommentare geschrieben werden:

<div>Hello World!{/* this is a comment */}</div>

Fragmente

Erlauben es einer Komponente, mehrere Elemente zurückzugeben (anstatt eines einzelnen Elements)

return (
  <>
    <td>Hello</td>
    <td>World</td>
  </>
);

JSX und Styling Grundlagen

JSX und Styling Grundlagen

In React-Projekten sind Style-Definitionen üblicherweise nahe bei den betroffenen Komponenten-Definitionen zu finden

Möglichkeiten:

  • CSS-Datei mit dem gleichen Namen wie die JSX-Datei
  • Stil-Definition am Beginn einer Komponentendefinitions-Datei
  • Inline Stil-Definition im Komponenten-Template

JSX und Styling Grundlagen

Styling in CSS-Dateien: typischerweise separate CSS-Datei für jede Komponente:

  • index.js
  • index.css (globale CSS-Deklarationen / -Resets)
  • App.js
  • App.css
  • TodoItem.js
  • TodoItem.css
  • ...

JSX und Styling Grundlagen

Einbinden von CSS-Deklarationen im Bundle:

// in TodoItem.js

import './TodoItem.css';

JSX und Styling Grundlagen

mögliche Struktur von CSS-Klassen via BEM: Blocks, Elements, Modifiers

<li
  className={
    isCompleted
      ? 'TodoItem TodoItem--Completed'
      : 'TodoItem'
  }
>
  <span className="TodoItem__Title">...</span>
  <input className="TodoItem__Checkbox" />
  ...
</li>

JSX und Styling Grundlagen

Werkzeuge, die bei Stylesheets hilfreich sein können:

  • classnames, clsx: zum Zusammensetzen von Klassennamen-Strings
  • CSS-Module: für "scoping"

JSX und Styling Grundlagen

CSS-in-JS: Stile werden in JavaScript definiert

Möglichkeiten:

  • grundlege Lösung: style-Property (Nachteile: keine Media-Queries, kein Autoprefixing, ...)
  • Library: emotion
  • Library: styled-components

JSX und Styling Grundlagen

In JSX weisen wir der style-Property ein Objekt zu, z.B.:

const containerStyle = {
  display: 'flex',
  justifyContent: 'center',
};

const imageStyle = {
  display: 'block',
};
<h1>Slideshow image {img}</h1>
<div style={containerStyle}>
  <button>prev</button>
  <img style={imageStyle} src="..." alt="..." />
  <button>next</button>
</div>

JSX und Styling Grundlagen

einfaches Beispiel mit der Library emotion:

import { css } from '@emotion/css';
<div
  className={css({
    display: 'flex',
    justifyContent: 'center',
  })}
>
  ...
</div>

Übung

Übung: Füge (weiteres) Styling zur Bilder-Slideshow hinzu

JSX: if / else

if / else

Möglichkeiten, um if / else in JSX zu verwenden:

  • Verwenden des ternären Operators (?) im Template
  • Vorbereiten von Werten mittels if / else bevor zu XML gewechselt wird
  • if / else, welches mehrere return-Statements beinhaltet - mit verschiedenen XML-Inhalten

if / else

inline-Bedingung:

<div>
  <h1>Foo Game</h1>
  {gameActive ? (
    <div>score: {score}</div>
  ) : (
    <div>Game over. Final score: {score}</div>
  )}
</div>

if / else

if / else Statement vor dem Template:

let statusMessage;
if (gameActive) {
  statusMessage = <div>score: {score}</div>;
} else {
  statusMessage = (
    <div>Game over. Final score: {score}</div>
  );
}

return (
  <div>
    <h1>FooGame</h1>
    {statusMessage}
  </div>
);

if / else

mehrere return-Statements:

if (gameActive) {
  return (
    <div>
      <h1>FooGame</h1>
      <div>score: {score}</div>
    </div>
  );
} else {
  return (
    <div>
      <h1>FooGame</h1>
      <div>Game over. Final score: {score}</div>
    </div>
  );
}

if

if - ohne else:

<div>{isError ? <div>{errorMessage}</div> : null}</div>

if

möglichere kürzere Version:

<div>{isError && <div>{errorMessage}</div>}</div>

Der Operator && in JavaScript:

true && 'my message'; // 'my message'

false && 'my message'; // false

Die Werte true und false werden in JSX nicht dargestellt (genauso wie null)

Übung

Übung: In der Slideshow, verstecke Buttons, die nicht angeklickt werden können (z.B. den previous-Button wenn der Benutzer beim ersten Bild ist)

JSX: Elemente wiederholen

Elemente wiederholen

Mehrere Elemente können via Arrays eingebunden werden:

const elements = [
  <li>foo</li>,
  <li>bar</li>,
  <li>baz</li>,
];
<h1>three elements</h1>
<ul>
  {elements}
</ul>

Elemente wiederholen

Beispiel: Auflisten aller Einträge des React-Objekts

Codesandbox: https://codesandbox.io/s/react-api-list-tjq60t?file=/src/ReactApiList.tsx

import * as React from 'react';
const reactApi = [];
for (let entry in React) {
  reactApi.push(<li>{entry}</li>);
}
<div>
  List of React API entries:
  <ul>{reactApi}</ul>
</div>

Elemente wiederholen

oft werden wiederholte Elemente aus Arrays von Daten via .map erstellt:

const initialTodos = [
  { id: 1, title: 'groceries', completed: false },
  { id: 2, title: 'cooking', completed: true },
  { id: 3, title: 'gardening', completed: false },
];

function TodoApp() {
  const [todos, setTodos] = useState(initialTodos);
  return (
    <ul>
      {todos.map((todo) => (
        <li>{todo.title}</li>
      ))}
    </ul>
  );
}

Elemente wiederholen

Bei obigem Code:
Warnung in der Browser-Konsole (Wegen Effizienz)

Lösung: key:

<ul>
  {todos.map((todo) => (
    <li key={todo.id}>{todo.title}</li>
  ))}
</ul>

Übung

Übung: für die Slideshow, erstelle kleine Vorschaubilder für 5 Bilder (2 vorhergehende, aktuelles, 2 folgende)

Hinweise: möglicher Code, um Vorschau-IDs zu generieren:

const previewIds = [];
for (let i = img - 2; i <= img + 2; i++) {
  if (i >= 0) {
    previewIds.push(i);
  }
}

JSX: Binden von Events

Events binden

function sayHello() {
  alert('hello world');
}
<button onClick={() => sayHello()}>Say Hello</button>

Liste von Browser-Events:
https://www.w3schools.com/jsref/dom_obj_event.asp

Events binden

Zugriff auf das Event-Objekt als Funktionsparameter:

<button
  onClick={(event) => {
    console.log(event);
  }}
>
  click me
</button>

hier wird das Event ein MouseEvent-Objekt sein

Events binden

Achtung: Ein Event Handler muss eine Funktion sein, und nicht ein Funktionsaufruf

OK:

<button onClick={(event) => handleEvent(event)}>
  click
</button>

OK:

<button onClick={handleEvent}>click</button>

nicht OK:

<button onClick={handleEvent()}>click</button>

Events binden

Standard-Verhalten eines Submit-Events in einem Formular: Direktes Senden von Daten zum Server

Ersetzen des Standardverhaltens:

<form
  onSubmit={(event) => {
    event.preventDefault();
    // handle submit with custom logic
  }}
>
  ...
  <button type="submit">submit</button>
</form>

Übungen

Slideshow Übungen:

versuche, die kurze oder lange Notation für Event-Listeners bei verschiedenen Events zu verwenden

frage das Event-Objekt einiger Events ab und logge einige Event-Informationen

kleines Formular, um auf ein bestimmtes Bild zu wechseln: beinhaltet ein Eingabefeld für eine Bild-ID und einen go-Button

JSX und Sicherheit

JSX und Sicherheit

mögliches Angriffsziel am Front-End: XSS-Angriffe

JSX und Sicherheit

XSS-Angriffe:

ein böswilliger Benutzer stellt Inhalte auf unserer Website online (z.B. in einem Post oder auf der Profilseite) - wenn andere die Website besuchen, wird der böswillige Code im Browser des jeweiligen Beutzers ausgeführt, während dieser unsere Website besucht

Demo für Experimente

JSX und Sicherheit

🙂 beim zuweisen von Inhalten werden XML-Tags automatisch escaped

der folgende Code wird nur reinen Textinhalt darstellen - er ist kein Angriffsziel:

const userAddress =
  'foo <script>prompt("enter credit card number:");</script>';
<h1>profile</h1>
<p>address: {userAddress}</p>

JSX und Sicherheit

🙁 das Attribut href bietet Angriffsziele:

const userWebsite =
  'javascript:prompt("enter credit card number:");';
<h1>profile</h1>
<p>address: {userAddress}</p>
<p><a href={userWebsite}>website</a></p>

mögliche Lösung: stelle sicher, dass User-generierte externe URLs mit http:// oder https:// beginnen

siehe auch: Artikel auf pragmaticwebsecurity.com, Artikel von Ron Perris, React pull request mit mehr Details

JSX: Kompilierung

JSX: Kompilierung

XML-Elemente werden kompiliert zu Aufrufen von:

  • _jsx() (ab React 17)
  • React.createElement() (React 16 - React muss importiert sein, um JSX zu schreiben)

JSX: Kompilierung

const element = <a href="https://google.com">Google</a>;

wird kompiliert zu:

const element = _jsx(
  'a',
  { href: 'https://google.com' },
  'Google'
);

JSX: Kompilierung

const element = (
  <MyComponent prop1={1} prop2={2}>
    <div>test 1</div>
    <div>test 2</div>
  </MyComponent>
);

wird kompiliert zu:

const element = _jsx(
  MyComponent,
  { prop1: 1, prop2: 2 },
  _jsx('div', null, 'test 1'),
  _jsx('div', null, 'test 2')
);