JSX

Topics

  • binding content and properties
  • whitespace, comments, escapes and fragments
  • if / else
  • repeating elements
  • styling basics
  • binding events
  • JSX and security
  • compilation

JSX

JSX = template language of React

  • an XML tag switches from JS to XML/HTML
  • curly braces switch back to JS

Valid elements in JSX

  • string
  • number
  • components (e.g. <div>, <img>, <MyComponent>)
  • Arrays of other elements
  • null, undefined, true, false (these are not rendered)

JSX: binding content and properties

Binding content

we can include numbers and strings as basic types:

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

Binding properties

we can change from XML to JS for an entire property:

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

Note: no quote characters around the value of href

Binding properties

If a prop is enclosed in quotes, the curly braces have no special meaning

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

Binding properties

setting boolean HTML properties:

<button disabled>disabled button</button>

or

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

Property names

Some element properties have different names than in HTML (reflecting standard DOM properties)

  • className (instead of class)
  • htmlFor (instead of for)

JSX: whitespace, comments and fragments

Whitespace

In HTML the following examples are equivalent (displaying a single space between the images):

<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

rules in JSX:

  • whitespace between two elements in one line is interpreted as a single space
  • whitespace between two elements on different lines is ignored

Single space:

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

no space:

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

Whitespace

"force" a space in JSX:

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

Escapes

Special characters like < can be inserted via JavaScript:

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

Comments

Comments can be written as JavaScript comments:

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

Fragments

Fragments enable returning multiple elements from a component / function:

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

JSX and styling basics

JSX and styling basics

In React, style definitions are usually located close to the component definition

possibilities:

  • CSS file with the same base name as the JSX file
  • style definition at the top of a component definition file
  • inline style definition in the component template

JSX and styling basics

styling in CSS files: typically separate CSS file for every component:

  • index.js
  • index.css (global CSS declarations / resets)
  • App.js
  • App.css
  • TodoItem.js
  • TodoItem.css
  • ...

JSX and styling basics

including CSS declarations in the bundle:

// in TodoItem.js

import './TodoItem.css';

JSX and styling basics

possible structure of CSS classes via BEM: Blocks, Elements, Modifiers

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

JSX and styling basics

tooling that can help with stylesheets:

  • classnames, clsx: for joining class name strings
  • CSS modules: for "scoping"

JSX and styling basics

CSS-in-JS: styles are defined inside JavaScript

options:

  • basic solution: style-property (downsides: no media queries, no autoprefixing, ...)
  • library: emotion
  • library: styled-components

JSX and styling basics

In JSX the style property takes an object, e.g.:

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 and styling basics

simple example with the emotion library:

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

Exercise

Exercise: Add (more) styling to the image slideshow application

JSX: if / else

if / else

options for using if / else in JSX:

  • use the ternary operator (?) in the template
  • prepare a value via if / else before switching to XML and use that value in the template
  • have an if / else with separate return statements in each branch

if / else

inline condition:

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

if / else

if / else statement before the 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

multiple 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 - without else:

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

if

potential shorter version:

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

The operator && in JavaScript:

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

false && 'my message'; // false

the values true and false are not rendered in JSX (just like null)

Exercise

exercise: in the slideshow, hide buttons which cannot be clicked (e.g. the previous-button if the user is viewing the first image)

JSX: repeating elements

Repeating elements

multiple elements may be added via arrays:

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

Repeating elements

example: listing all entries in the react package

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>

Repeating elements

typically, repeated elements are created from arrays of data via .map:

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>
  );
}

Repeating elements

With the above code:
warning in the browser console (concerning efficiency)

solution: key:

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

Exercise

exercise: for the slideshow, create small previews for 5 images (2 previous, current, 2 upcoming)

hint: example code for creating preview ids:

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

JSX: Binding events

Binding events

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

list of browser events: https://www.w3schools.com/jsref/dom_obj_event.asp

Binding events

accessing the event object as a function parameter:

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

here the event will be a MouseEvent object

Binding events

note: an event handler must be a function, not a function call

OK:

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

OK:

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

not OK:

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

Binding events

Default behavior of a submit event in a form: directly send data to the server

Replacing the default behavior:

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

Exercises

slideshow exercises:

try using the short or long notation for event listeners on different events

get the event object of some event and log some event details

small form to go to a specific image: has an input field to enter an image ID and a go button

JSX and security

JSX and security

potential threat on the front-end: XSS attacks

JSX and security

XSS attacks:

a malicious user submits some content to our website (e.g. in a post or on their profile page) - when other users visit the site, the malicious code will be executed in the other user's browser while visiting our website

demo for experiments

JSX and security

🙂 when binding content, XML tags will be escaped automatically

the following code will just display plain text content - it is not an attack target:

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

JSX and security

🙁 the attribute href does offer attack targets:

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

possible solution: make sure user-supplied external URLs start with http:// or https://

see also: article on pragmaticwebsecurity.com, article by Ron Perris, React pull request with more details

JSX: compilation

JSX: compilation

XML elements are compiled to calls of:

  • _jsx() (React 17 and above)
  • React.createElement() (React 16 - React must be imported when writing JSX)

JSX: compilation

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

compiles to:

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

JSX: compilation

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

compiles to:

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