React Suspense:

The Interactive Experience TM

πŸ™

DISCLAIMER

πŸ™

"Civilization advances by extending the number of important operations which we can perform without thinking about them."

Alfred North Whitehead

What does React

let you do without thinking?

πŸ™

Q: Why Do We Like React?

or any other framework

πŸ™

Vanilla JS

hard to reason about

const mapboxgl = require("mapbox-gl");
const buildMarker = require("./marker");
const attractions = require("./attractions");

/*
 * App State
 */

const state = {
  hotels: [],
  restaurants: [],
  activities: []
};

/*
  * Instantiate the Map
  */

mapboxgl.accessToken = "pk.eyJ1IjoiY2Fzc2lvemVuIiwiYSI6ImNqNjZydGl5dDJmOWUzM3A4dGQyNnN1ZnAifQ.0ZIRDup0jnyUFVzUa_5d1g";
const map = new mapboxgl.Map({
  container: "map-canvas",
  center: [-74.0, 40.731],
  zoom: 12.3, // starting zoom
  pitch: 35,
  bearing: 20,
  style: "mapbox://styles/mapbox/streets-v10"
});

/*
  * Populate the list of attractions
  */

attractions.load().then(list => {
  list.hotels.forEach(attraction => makeOption(attraction, "hotels-choices"));
  list.restaurants.forEach(attraction => makeOption(attraction, "restaurants-choices"));
  list.activities.forEach(attraction => makeOption(attraction, "activities-choices"));
});

function makeOption(attraction, selector) {
  const option = new Option(attraction.name, attraction.id); // makes a new option tag
  const select = document.getElementById(selector);
  select.add(option);
}

/*
  * Attach Event Listeners
  */

// what to do when the `+` button next to a `select` is clicked
["hotels", "restaurants", "activities"].forEach(addEventHandlerFor);
function addEventHandlerFor(attractionType) {
  document.getElementById(`${attractionType}-add`).addEventListener("click", () => handleAddAttraction(attractionType));
}

// Create attraction assets (itinerary item, delete button & marker)
function handleAddAttraction(attractionType) {
  const select = document.getElementById(`${attractionType}-choices`);
  const selectedId = select.value;

  // Find the correct attraction given the category and ID
  const selectedAttraction = attractions.find(attractionType, selectedId);

  // If this attraction is already on state, return
  if (state[attractionType].find(attractionData => +attractionData.id === +selectedId)) return;

  //Build and add attraction
  buildAttractionAssets(attractionType, selectedAttraction);
}

function buildAttractionAssets(category, attraction) {
  // Create the Elements that will be inserted in the dom
  const removeButton = document.createElement("button");
  removeButton.className = "btn btn-xs btn-danger remove btn-circle";
  removeButton.append("x");

  const itineraryItem = document.createElement("li");
  itineraryItem.className = "itinerary-item";
  itineraryItem.append(attraction.name, removeButton);

  // Create the marker
  const marker = buildMarker(category, attraction.place.location);

  // Create an 'attractionData' object that will be added to the state.
  // It contains the assets created plus all the original selected attraction data.
  const attractionData = Object.assign(
    {
      assets: {
        itineraryItem,
        marker
      }
    },
    attraction
  );

  // Adds the attraction to the application state
  state[category].push(attractionData);

  addAttractionToDOM(category, attractionData);
  removeButton.addEventListener("click", function remove() {
    // Stop listening for the event
    removeButton.removeEventListener("click", remove);

    // Remove the current attrction from the application state
    const index = state[category].indexOf(attractionData);
    state[category].splice(index, 1);

    removeAttractionFromDOM(category, attractionData);
  });
}

function addAttractionToDOM(category, attractionData) {
  // Append attraction elements to the DOM & Map
  document.getElementById(`${category}-list`).append(attractionData.assets.itineraryItem);
  attractionData.assets.marker.addTo(map);

  // Animate the map
  map.flyTo({ center: attractionData.place.location, zoom: 15 });
}

function removeAttractionFromDOM(category, attractionData) {
  // Remove attraction's elements from the dom & Map
  attractionData.assets.itineraryItem.remove();
  attractionData.assets.marker.remove();

  // Animate map to default position & zoom.
  map.flyTo({ center: [-74.0, 40.731], zoom: 12.3 });
}
function buildAttractionAssets(category, attraction) {
  // Create the Elements that will be inserted in the dom
  const removeButton = document.createElement("button");
  removeButton.className = "btn btn-xs btn-danger remove btn-circle";
  removeButton.append("x");

  const itineraryItem = document.createElement("li");
  itineraryItem.className = "itinerary-item";
  itineraryItem.append(attraction.name, removeButton);

  // Create the marker
  const marker = buildMarker(category, attraction.place.location);

  // Create an 'attractionData' object that will be added to the state.
  // It contains the assets created plus all the original selected attraction data.
  const attractionData = Object.assign(
    {
      assets: {
        itineraryItem,
        marker
      }
    },
    attraction
  );

  // Adds the attraction to the application state
  state[category].push(attractionData);

  addAttractionToDOM(category, attractionData);
  removeButton.addEventListener("click", function remove() {
    // Stop listening for the event
    removeButton.removeEventListener("click", remove);

    // Remove the current attrction from the application state
    const index = state[category].indexOf(attractionData);
    state[category].splice(index, 1);

    removeAttractionFromDOM(category, attractionData);
  });
}

πŸ™

ReactJS

Helps us Mount/Unmount

import React, {Component} from 'react'
import Navbar from './Navbar'
import Map from './Map'
import Panel from './Panel'
const attractions = require("./attractions");
const buildMarker = require("./marker");

export default class extends Component {
    constructor() {
        super()
        this.state = {
          hotels: [],
          restaurants: [],
          activities: []
        }
        this.addFunc = this.addFunc.bind(this)
        this.delFunc = this.delFunc.bind(this)
    }
    addFunc(attractionType) {
        const selectedId = document.getElementById(attractionType + '-choices').value
        const selectedAttraction = attractions.find(attractionType, selectedId);
        const newMarker = buildMarker(attractionType, selectedAttraction.place.location)
        newMarker.addTo(this.map)
        selectedAttraction.marker = newMarker
        let newstate = Object.assign({}, this.state)
        newstate[attractionType].push(selectedAttraction)
        this.setState(newstate)
        this.map.flyTo({ center: selectedAttraction.place.location, zoom: 15 });
    }
    delFunc(attractionType, id) {
        let newstate = Object.assign({}, this.state)
        newstate[attractionType] = newstate[attractionType].filter(x => {
            if (x.id != id) return true
            else x.marker.remove()
            return false
        })
        this.setState(newstate)
    }
    render() {
        return (<div>
                    <Navbar />
                    <div id="app" className="clearfix">
                        <Map ref={(x) => !x ? '' : this.map = x.map}/>
                        <Panel selections={this.state} addFunc={this.addFunc} delFunc={this.delFunc}/>
                    </div>
                </div>)
    }
}

State

Handlers

Render

πŸ™

What is Async Rendering?

πŸ™

What is Async Rendering?

πŸ™

What is Async Rendering?

πŸ™

What is Async Rendering?

πŸ™

Don't confuse with Async Data Fetching.

We've always had that.

Β 

We're talking about React doing its rendering, asynchronously.

Β 

that is the new thing.

Async Rendering

A Brief History

πŸ™

Async Rendering

A Brief History

πŸ™

Async Rendering

A Brief History

πŸ™

Async Rendering

A Brief History

πŸ™

Async Rendering

March 2018

πŸ™

What is React Suspense?

A generic way for components to suspend rendering while they load async data.

πŸ™

What is React Suspense?

A fundamentally new capability that

  • lets you render a component tree β€œin background”
  • while components are fetching data, and
  • display them only after the whole tree is ready.
  • For slow connections, it gives you full control over where and when to show a placeholder.
  • It doesn’t destroy the previous view while this is happening.

πŸ™

Who made React Suspense?

πŸ™

Q: When can I have it?

A: By end 2018

πŸ™

Q: I don't like it/have time to learn it. Do I have to use it?

A: No. It is 100% opt-in.

(A: BUT it is very cool!)

πŸ™

ah I forgot the cheat sheet data.

Let me go fetch it

Β 

I am notΒ throwing away my shot.

I promise I'll be back.

πŸ’©

React Suspense Cheat Sheet

Benefits/Uses βš’οΈ

  • Data Fetching⭐  Β 

    • Code splitting

    • Img loading

    • CSS loading

  • Fix race conditions

  • Debouncing

  • Intentional states⭐

  • Preloading

  • Less boilerplate

  • Streaming SSR ⚠️

  • what else??⭐⭐⭐

Goals πŸ“Š

  • Render when ready⭐

  • Fetch in render

  • No race default⭐

  • Precise control⭐

  • Speculative fetch

  • Interactive AF⭐

Glossary πŸ€–
 -  Idempotency != Purity
 -  Algebraic Effects
 -  Coroutines⭐
 -  Reconciliation
 -  Double Buffering
 -  Expiration ⚠️

Code πŸ‘©β€πŸ’»

Primitives

 -  React.Timeout⭐              

Β -Β  React.AsyncMode

Low Level API

 -  simple-cache-provider⭐

 -  Apollo/Relay cache⭐⭐

 -  what else?? ⭐⭐⭐⭐

High Level API (../future)

Β -Β  createFetcher()

Β -Β  <Placeholder delayMs>

Β -Β  <Loading />

Β -Β  this.deferSetState()

 -  <div hidden={true}> ⚠️

Β 

Impact on Libraries⭐⭐

New Devtools⭐⭐⭐

by @swyxΒ πŸ™

πŸ™

"Civilization advances by extending the number of important operations which we can perform without thinking about them."

Alfred North Whitehead

What does React Suspense

let you do without thinking?

πŸ™

Pit of Success

"In stark contrast to... a journey across a desert to find victory through many trials and surprises, we want our customers to simply fall into winning practices by using our framework."

"AΒ well-designed system makes it easy to do the right things and annoying (but not impossible) to do the wrong things."

πŸ™

Links & Acknowledgements

πŸ™

Thank You

πŸ™

React Suspense: The Interactive Experience

By Shawn Swyx Wang

React Suspense: The Interactive Experience

  • 5,060