Tue 26 Sep 2017 — Mon 30 Apr 2018

React JS

Uses ES6, with classes everywhere. Unfortunately that means we have to use the this keyword, which means we have to call .bind(this) everywhere.

Components, Elements and Rendering

React.render(element, parent) draws a thing on the screen. It uses its Virtual DOM to calculate a small change which it can make to the web page's DOM.

Elements are immutable - they don't change over time.

We create an element with React.createElement. An element has:

  • An HTML tag name or a React Component class.
  • A dictionary of properties and event handlers
  • Contents (inner HTML, can be referred to with the special props.children attribute).

Components are a function which takes a props input and outputs a React Element. They may be wrapped in an ES6 class, in which case they inherit from React.Component and their drawing function goes in the render() method.

ES6 classes have a constructor, so if you use them to create a React Component then you should call super(props) to pass the props to the base class.

Stateful Components

If you wrap a component in a class, then it can have some internal state this.state, and will be re-rendered whenever that changes.

You change the state using this.setState({ my: "new state"}). Don't assign directly to this.state, unless you are in the constructor.

You can pass a function to setState if you want the new value to depend on the previous. There is a dictionary merging behaviour here: if you want to remove a property, make sure to explicitly clear it.

this.setState(function(previousState, props) {
    return {
        counter: previousState.counter + 1,
        stateToDestroy: null
    };
});

In order to make this state tracking work with arrays of elements, you should set the key property on each of those elements. If you get this wrong, React will have to make more changes to the actual DOM.

State can flow down to child components. It's not clear how this works without JSX?

Stateful components can have some other methods:

componentDidMount()
called after rendering the component
componentWillUnmount()
called just before removing the component

Forms

The idiomatic way to handle forms in React is for the React Component's state to be the source of truth.

To make this work, we hook the onChange event for each input to update the React state.

React has some special behaviours for HTML's more stupid inputs:

textarea
invents a value attribute (replaces innerHTML).
select
has a selected attribute (replaces iterating over the option elements to find the selected one).

React invents a value property for the textarea element.

JSX

This is a little HTML templating language which you write inline in your JavaScript.

It spots curly braces { this.whatever } and treats them as escaping back to JavaScript.

It escapes anything you pass to it.

I'm ignoring it, because yuck.

Refs

Each class component has a ref attribute, which takes a callback. It will be called when the component is mounted or unmounted.

This is useful for doing some things, like selection, but is to be avoided in most cases.

Reagent for Clojurescript

Components are functions which return vectors.

The vectors have three parts:

  • A symbol denoting the tag like :div.
  • A dictionary containing attributes
  • Their contents

Seqs will be expanded, so you can use e.g. for inside the vector contents.

The vector can contain the name of another component.

Components can also be a function which itself returns a rendering function.

Once you have defined some components, call (reagent.core/render [my-root-component]).

Ok, this looks really good.

Unique Keys

To identify items in a collection, we can specify a key in two ways:

  • Attach as metadata with ^{:key "some-unique-id"} [:div {} "Hello I am some text in a div"].
  • Specify in attribiutes map like [:div {:key "some-unique-id"} "Hello I am some text in a div"]

State

Reagent has a special type of atom reagent.core/atom.

You can operate on these useing the normal atom functions like swap! or reset!.

Watching for state changes is easier than in normal react. Reagent compares the atoms with identical?.

It is safe to share between many components. We can either pass them around as arguments, as put them in global scope with defonce.

Events

Events are hyphenated. As with React, you specify them in the attributes dictionary.

Server-side Pre-render

reagent.dom.server/render-to-string

Re-frame for Clojurescript

This is some extra stuff that works with Reagent.

https://github.com/Day8/re-frame

It adds some sort of data-flow/events thingy on top of atoms.

Fire events
(re-frame.core/dispatch [:my-event {:my-data 5}])
Handle events
(re-frame.core/reg-event-fx :my-event (fn [state my-event-with-my-data] {:db (do-something)})) returns a map of effects.
  • (re-frame.core/reg-event-db :my-event (fn [db my-event-with-data] ...) is a simpler version that always deals the apps main state
Handle effects
you write some effect handlers yourself.
Query state
(re-frame.core/reg-sub :my-query (fn [] do-something))
Views
(defn component [] (let [my-data (subscribe [:my-query query-parameter])] [:div my-data]))
React
does its DOM diffing thing.

All the state goes together in one lump. app-db is the atom that holds the app state. db is the state itself.

When the state changes, there is a thing called a signal graph. Each state querying thing can in turn depend on some other queries (they default to depending on the app-db itself). Good.

If we have a lot of work to do, we should do it in event handlers. We can break it up, do some of it, then redispatch the event to pick up the rest. In-between, the browser will have time to re-render.

When combining re-frame with stateful JS libraries, you need two components:

outer
gets the data
inner
connects the data up to the JS library's object