Hackbright Engineering June 2018

React

React

Intro

Why This Is Useful

  • JavaScript is awesome.
    • So awesome you might start using a lot of it in an app.
      • So much it might start to become a tangled mess.
        • Such a tangled mess it’ll be hard to debug and re-use your code later.

Goals

  • Front End Frameworks
  • React Concepts
    • Components
    • Properties
    • State
  • Demo React Apps
  • Comparing React to other libraries & frameworks

Anti-Goals

  • Syntax
  • Tools

This lecture is much about why and the big ideas.

You can learn the syntax as you need it.

In particular, the thinking around breaking applications into components is probably the most important part to learn about.

Front End Frameworks

  • Larger JS libraries
  • Provide “blueprint” for apps
  • “Opinionated”
    • “This is how you should design a JS app”
  • Often: provide for code re-use
  • Often: provide templating of HTML (like Jinja)

React

_images/react.svg

Popular, powerful front-end framework.

Developed by and sponsored by Facebook.

  • Make it easy to make reusable “view components”
    • These “encapsulate” logic and HTML into a class
  • Often make it easier to build modular applications

Components

  • The building blocks of React
  • Pieces of UI & view logic
  • Classes that know how to render themselves into HTML
class Cat(object):
    """Feline catus."""

    def render(self):
        return "<div>...</div>"

Demo: Hello

react-demo/hello/index.html
<!DOCTYPE html>
<html>
<body>

<h1>Demo: Hello</h1>

<p>I made a React component!</p>

<div id="root"> <!-- component will go in this div --> </div>

<p>Isn't it neat?</p>

<script src="http://unpkg.com/react@15.6.2/dist/react.js"></script>
<script src="http://unpkg.com/react-dom@15.6.2/dist/react-dom.js"></script>

<script src="http://unpkg.com/babel-standalone"></script>

<script src="hello.jsx" type="text/jsx"></script>

</body>
</html>

A component is a React class
with a render method:

react-demo/hello/hello.jsx
var Hello = React.createClass({
    render: function () {
        return <p>Hi World!</p>
    }
});

We add our component to HTML with ReactDOM.render:

react-demo/hello/hello.jsx
ReactDOM.render(
    <Hello />,
    document.getElementById("root")
);

JSX

var Hello = React.createClass({
    render: function () {
        return <p>Hi World!</p>
    }
});
ReactDOM.render(
    <Hello />,
    document.getElementById("root")
);

What’s this HTML in our JavaScript?

JSX is like HTML embedded in JavaScript:

if (score > 100) {
    return <b>You win!</b>
}

You can also “re-embed” JavaScript in JSX:

if (score > 100) {
    return <b>You win, { playerName }</b>
}

(looks for JavaScript variable playerName)

Using JSX

  • JSX isn’t legal JavaScript
    • It has to be “transpiled” to JavaScript
  • You can do this with Babel

Transpiling JSX in Browser

  • Awesome for development — nothing to install!

  • Load Babel standalone library:

    <script src="http://unpkg.com/babel-standalone"></script>
    
  • Mark JSX files with type="text/jsx":

    <script src="hello.jsx" type="text/jsx"></script>
    
  • Read handouts to learn how to do on command line

Use Babel on Command Line

While it’s convenient to transpile JSX into JavaScript directly in the browser like this, it’s not suitable for real-world deployment: it takes a second to do the conversion, leading to a poor experience for users.

Better for deployment is to convert JSX to JavaScript once, via the command line, and then save and use the converted JS directly.

To do this:

  1. You need to install npm

  2. Then use npm to install Babel and settings for React:

    $ npm install babel-cli babel-preset-react
    
  3. To convert a file:

    $ ./node_modules/.bin/babel --presets=react file.jsx > file.js
    

Properties

a.k.a. Props

A useful component is a reusable one.

This often means making it configurable or customizable.

react-demo/hello/hello.jsx
var Hello = React.createClass({
    render: function () {
        return <p>Hi World!</p>
    }
});

It would be better if we could configure our greeting.

Our greeting will be Hi ______ from ______.

Let’s make two “properties”:

to
Who we are greeting
from
Who our greeting is from

Demo: Hello-2

react-demo/hello-2/hello.jsx
var Hello = React.createClass({
    render: function () {
        return <p>Hi { this.props.to } from { this.props.from }</p>
    }
});

Set properties on element; get using this.props.propName.

react-demo/hello-2/index.jsx
ReactDOM.render(
    <Hello to="World" from="Joel"/>,
    document.getElementById("root")
);

Reusing Component

You can use a component many times:

Adding several at once:

ReactDOM.render(
    <div>
        <Hello to="Kay" from="Kim" />
        <Hello to="me" from="you" />
    </div>,
    document.getElementById("root")
);

Adding to different places:

ReactDOM.render(
    <Hello to="Kay" from="Kim" />,
    document.getElementById("root")
);

ReactDOM.render(
    <Hello to="me" from="you" />,
    document.getElementById("also")
);

Properties Requirements

  • Properties are for configuring your component

  • Properties are immutable

  • Properties can be strings:

    <User name="Jane" />
    
  • For other types, embed JS expression using the curly braces:

    <User name="Jane" salary={ pay * 12 }
        hobbies={ ["bridge", "Python", "tea"] } />
    

State

Demo: Dice

  • We’re making a role playing game, __Dunder__ & Dragons
    • So we need components of dice users can roll
  • Dice come in different numbers of sides (4, 6, 8, 10, 12, 20)
_images/dice.png

When you add a die, you can choose the number of sides.

Let’s make this a property.

sides
Number of sides of die
react-demo/dice/die.jsx
ReactDOM.render(
    <div>
        <Die sides="4" />
        <Die sides="6" />
        <Die sides="8" />
        <Die sides="10" />
        <Die sides="12" />
        <Die sides="20" />
    </div>,
    document.getElementById("root")
);
  • Users should click on a die to “roll” it & see results
  • The result can change every time they roll
    • So it’s not a property — those can’t change
  • This is a “state” of the component
result
Current result of die (default ”?”)
react-demo/dice/die.jsx
var Die = React.createClass({

    getInitialState: function () {
        return {result: "?"}
    },

    roll: function () {
        var num = Math.ceil(
            Math.random() * this.props.sides);
        this.setState({result: num});
    },

    render: function () {
        return (
            <button className="die" onClick={ this.roll }>
                <i>d{ this.props.sides }</i>
                <b>{ this.state.result }</b>
            </button>
        );
    },
});

How this works:

  • Property sides = # of sides
  • getInitialState = initial state
    • Called by React at start
    • result: ”?” until rolled
  • Click handler, roll, on button
    • Generates num 1...sides
    • setState updates state
  • React re-renders die
    • Since state changed

Updating State

  • React determines when it needs to re-render
    • You don’t have to explicitly track this
    • It is very efficient about this
  • Always update state with this.setState({...})
    • Never directly with this.state = ...
    • Otherwise, React may not notice change

Properties v State

Properties are set by the parent, in the JSX.
are immutable, and are for configuration.

State has an initial value, and changes over lifetime.

Changes to state trigger re-rendering of the component (and its children, if any).

SharkWords

Let’s examine a game built with React.

_images/sharkwords.png

Properties

answer
Our randomly-chosen secret word
react-demo/sharkwords/sharkwords.jsx
var word = randomWord();
console.log("answer = " + word);

ReactDOM.render(
    <SharkWords answer={ word }/>,
    document.getElementById('root'));

The random word comes from our randomWord function in words.js. This is just pure JS, unrelated to React.

State

numWrong
Number of wrong guesses made (default 0)
guessed
Set of all guessed letters (default empty set)
react-demo/sharkwords/sharkwords.jsx
var SharkWords = React.createClass({

    getInitialState: function () {
        return {numWrong: 0, guessed: new Set()}
    },

    guessedWord: function () {  // "app_e" for "apple"
        var word = "";

        for (var ltr of this.props.answer)
            word += this.state.guessed.has(ltr) ? ltr : "_";

        return word;
    },

    handleGuess: function (evt) {
        var letter = evt.target.value;
        this.state.guessed.add(letter);
        this.setState({guessed: this.state.guessed});

        if (this.props.answer.indexOf(letter) === -1)
            this.setState({'numWrong': this.state.numWrong + 1});
    },
});

Ternary Operators and Sets

This snippet uses two JavaScript features you may not be familiar with. Neither are relatd to React, but they can be useful:

  • Ternary Operator: expression ? true-val : false-val. This evaluates the expression and, if it evaluates to true, returns the true-val, otherwise it returns the false-val. These can be a handy way to compact an if/then/else block into a single line.
  • Sets: the newest version of JavaScript adds sets to the core language. These work very much like Python sets. To ask if a value is in a set, use Set.has(val).

Rendering

_images/sharkwords-design.png
react-demo/sharkwords/sharkwords.jsx
var SharkWords = React.createClass({

    render: function () {
        return (
            <div className="sharkwords">
                <h1>Sharkwords!</h1>
                <img src={ "guess" + this.state.numWrong + ".png" }/>
                <p className="guessed">
                    Wrong guesses: { this.state.numWrong }
                </p>
                <p className="word">{ this.guessedWord() }</p>

                { this.renderButtons() }
            </div>
        )
    },
});

The rendering of the list of buttons is in a separate function just because it seemed clearer to break that out for readability; it could have been inlined here.

react-demo/sharkwords/sharkwords.jsx
var SharkWords = React.createClass({

    renderButtons: function () {
        var buttons = [];

        for (var ltr of "abcdefghijklmnopqrstuvwxyz")
            buttons.push(
                <button key={ ltr } value={ ltr }
                        disabled={ this.state.guessed.has(ltr) }
                        onClick={ this.handleGuess }>
                    { ltr }
                </button>)

        return <div className="letters">{ buttons }</div>;
    },
});

“Key” attribute

A small thing to note is the key attribute in <button key={ ltr } ...>.

When React elements are made in a loop, it is very helpful for React to have a key for each. This is a unique value for each individual item, and allows React to more quickly decide which items to re-render later. Since each button has a different letter of the alphabet associated with it, we simply use that as this id-like key value.

Debugging With Chrome Dev Tools

Chrome Developer Tool is an awesome tool for debugging React apps.

_images/devtool.png

Ubermelon Site

An advanced demo of Flask and React.

Let’s take a non-codeful look at what it can teach you.

Single Page App

  • Entire site is really one page
    • Changing pages just swaps out content
    • JS updates the browser URL to look like you moved
  • This can be faster for users to move around
  • Add on product, React-Router provides this

Flask Backend

  • Flask serves this app with only 3 routes:
    • /: Main app HTML (returns simple Jinja template)
    • /melons.json: Returns JSON for melon price table
    • /post-comment.json: Handles posting of comment & returns status message
  • Everything else is JS/CSS in static/ folder
    • So Flask mostly just handles JSON data

Does the homepage need Jinja?

The main app HTML goes through Jinja templating in the current app — but, really, this is overkill, since it’s completely static and uses no Jinja templating variables. This doesn’t pose a problem, but if you wanted to, you could store index.html in static/ instead of templates/, and change the route to:

@app.route("/")
def homepage():
    return send_static_file("index.html")

(send_static_file is a function you import from Flask; it returns a response with the named file from the static folder)

react-demo/ubermelon/server.py
@app.route("/")
def homepage():
    return render_template("index.html")

@app.route("/melons.json")
def get_melons():
    melons = [
        {"type": "Watermlon", "price": randint(2, 20) / 2.0},
        {"type": "Muskmelon", "price": randint(2, 5) / 2.0},
        {"type": "Pupmelon", "price": randint(2, 80) / 2.0},
        {"type": "Lemonmelon", "price": randint(2, 9) / 2.0}
    ]
    return jsonify(melons=melons)

@app.route("/post-comment.json", methods=["POST"])
def post_comment():
    comment = request.data

    # we could save this in a database here

    return jsonify(message="Saved.")

Impact on App Design

Backend-driven app

  • Server does lots of view logic
  • Server renders HTML
    • In Flask, using Jinja
  • JS does some cleanup/light adding

Frontend-driven app

  • Front-end has view logic
  • Server provides an API (to get JSON or other data for JS)
  • Server sends static HTML, JS

Also in Ubermelon Site

Unrelated to React, the Ubermelon site also demonstrates some newer JS features:

  • Arrow functions (nicer syntax for inline functions)
  • Array.map for looping nicely in JSX
  • Making AJAX Calls Without jQuery

Comparing React

jQuery v React

jQuery

  • “Imperative”
    • What steps to do, in what order
    • As data changes, change DOM directly
  • Lots of CSS IDs/classes for JS hooks
  • Easy to learn
  • Can be harder to re-use parts
  • Super-popular, here to stay

React

  • “Declarative”
    • Describe how something renders
    • React decides when to render
  • CSS IDs/classes generally just for CSS
  • More to learn
  • Often easier to re-use components
  • Popular and newer

React v Angular2

React

  • Library
  • Provides “view” component
    • Rendering logic
  • Doesn’t provide model
  • Often needs other JS libraries
  • Can be simpler to start with
  • Popular and excellent

Angular2

  • Framework
  • Provides view component
    • Also: model & controller components
  • Includes other parts
    • Single page app routing
    • Built-in AJAX
    • Form validation
  • More to learn, does more for you
  • Popular and excellent

Learning More

Other React Features

  • Animation
  • Help for writing JS tests
  • Hundreds of plugins & add-ons

Great Places to Tinker

  • Rebuild Sharkwords
    • Re-creating helps strengthen syntax
  • Make a word-scramble game
    • Practice making components
  • Study Ubermelon site
    • Learn AJAX, routing, other ideas
  • Read online tutorials & docs

Learning More

Different Style of Making Classes

We’ve shown building components in the “classic” way:

var Hello = React.createClass({
    render: function () { /* ... */ }
});

There’s also a version using syntax from ES6, the very-newest version of JavaScript:

class Hello extends React.Component {
    render() { /* ... */ }
}

Tutorials and books vary on which they show.

They do the same things and you can use either. You can mix and match them in the same project.