React Training

LDS Stack Team

Slides

Prerequisites

  • Node.js
    • node 8+, npm 5.2+
  • git
  • ldsjs
    • npm i -g ldsjs --registry=http://icsnpm.ldschurch.org
  • Google Chrome
    • React Developer Tools Chrome plugin
    • Redux DevTools Chrome plugin
    • Apollo DevTools Chrome plugin
  • Text editor of your choice
    • Visual Studio Code if you need a recommendation

System Check

Run these commands

node --version # >= 8.x.x
npm --version # >= 5.2.x
ldsjs --version # >= 1.1.3

Create a project

ldsjs react bootcamp-labs
cd bootcamp-labs
git init
git add -A
git commit -m "Initial commit"
npm install
npm run dev

Setup

Scripts

With the Starter

then open localhost:3000 to view app

npm run storybook

then open localhost:9999 to view storybook

Add the labs

Modern JavaScript

Part 1

ES Modules

Modern JavaScript

The Past

due to a lack of a module system in Javascript, two competing workarounds had been developed: CommonJS (node), and AMD (RequireJS).

 

The following is an example of a CommonJs module, which is commonly found in Node.js on the server, and Browserify on the client.

// lib.js (the export)

module.exports = {
   lib: () => { ... }
};
// file.js (the import)

var { lib } = require('./lib.js');

ES Modules

An ES6 module is a file containing JS code. There’s no special module keyword; a module mostly reads just like a script. There are two differences.

 

ES6 modules are automatically strict-mode code, even if you don’t write "use strict"; in them.

 

You can use import and export in modules.

 

 

ES Modules

An ES6 module is a file containing JS code. There’s no special module keyword; a module mostly reads just like a script.

// lib.js (the exporter)

export default () => {}
export const mySpecificFunction = () => {}
export { objA, objB, funcC, funcD }
// file.js (the importer)

import lib from './lib.js';
import { mySpecificFunction } from './lib.js';
import lib, { mySpecificFunction } from './lib.js';
import lib, { mySpecificFunction, objA } from './lib.js';
import * as everything from './lib.js';
import lib, * as everything from './lib.js';
import { mySpecificFunction as msf } from './lib.js';

ES Modules

modules can both import and export

import dep from "./my-deps";

export default () => {}
export const mySpecificFunction = () => {}

ES Modules

Import statements must be first (at the top of a file)

ES Modules

Everything declared inside a module is local to the module, by default. If you want something declared in a module to be public, so that other modules can use it, you must export that feature.

// file.js
// only in this module's scope
const mySecretVariable = "hello";

export const mySpecificFunction = () => {}

Resources

Arrow Functions

Modern JavaScript

//square named function
function square(a) {
  return a * a
}

//square function expression
const square = function(a) {
  return a * a;
}

//function expression using arrow syntax
const square = (a) => {
  return a * a;
};

//with Implicit Return
const square = (a) => a * a;

//with no parameter parenthesis
const square = a => a * a;

console.log(square(4)); //16

Functions in JavaScript

The functions to the left are all functionally equivalent. They accept a number, and return the square of that number.

Arrow Functions

  • An arrow function expression has a shorter syntax compared to function expressions and does not bind its own this, arguments, super, or new.target.
  • Arrow functions are always anonymous.
  • These function expressions are best suited for non-method functions* and they can not be used as constructors.
param => true;

param => {return true};

(param1, param2) => (param1 + param2);

() => null;

() => { return true; }

() => (true)

() => ({ a: 1, b: 2 })

It will be helpful to be able to recognize different variations of function signatures by sight, and see that they all fall into the same pattern:

Recognize the Pattern

Some Helpful Conventions

If you are new to this syntax, consider always wrapping the parameters in parenthesis, and using a function body with an explicit return.  This verbose style will work everywhere

const square = (a) => {
  return a * a;
}

[].map((elem) => {
  return {
    a: elem.a,
    b: elem.deep.b
  }
});

Which Should I Use?

The recommendation is to use the appropriate syntax for the situation.

// scales verbosely
[].map((x, y) => {
  return x * y;
});

// scales nicely
[].map( (x, y) => x * y);

lab

Resources

  • Similar to  self  in languages like Python and C#

  • this is usually the object a method is called on

  • There are lots of exceptions to the value of this
  • See MDN for more information (link in Resources)

Keyword

this

        Keyword -

Arrow Functions

this

In arrow functions, this retains the value of the enclosing lexical context's this. In global code, it will be set to the global object.

//Using Arrow Function
function Person(){
  this.age = 0;

  setInterval(() => {
// |this| refers to the current Person object
    this.age++; 
  }, 1000);
}

var p = new Person();
//No Arrow (setting this to a variable)
function Person() {
  var that = this;
  that.age = 0;

  setInterval(function growUp() {
    // The callback refers to 
    // the `that` variable of which
    // the value is the expected object.
    that.age++;
  }, 1000);
}

var p = new Person();

When a function is called as a method of an object, its this is set to the object the method is called on.

Keyword - Extra Info

this
var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};
console.log(o.f()); // 37

When a function is used as an event handler, its this is set to the element the event fired from

// When called as a listener, 
// turns the related element blue
function bluify(e) {
  // Always true
  console.log(this === e.currentTarget); 
  // true when currentTarget and 
  // target are the same object
  console.log(this === e.target);
  this.style.backgroundColor = '#A5D9F3';
}

// Get a list of every element in the document
var elements = document.getElementsByTagName('*');

// Add bluify as a click listener so when the
// element is clicked on, it turns blue
for (var i = 0; i < elements.length; i++) {
  elements[i].addEventListener('click', bluify, false);
}

Keyword - Extra Info

this

Where a function uses the this keyword in its body, its value can be bound to a particular object in the call using the call or apply methods which all functions inherit from Function.prototype.

// An object can be passed 
// as the first argument
// to call or apply and 
// this will be bound to it.
var obj = {a: 'Custom'};

// This property is set 
// on the global object
var a = 'Global';

function whatsThis() {
  return this.a;  
}

whatsThis();          // 'Global'
whatsThis.call(obj);  // 'Custom'
whatsThis.apply(obj); // 'Custom'

Array Methods

Modern JavaScript

Array Methods

  • map
  • filter
  • reduce

map

items.map( x => x * x )

The map() method creates a new array with the results of calling a provided function on every element

The new array will be the same size as the original array

map

items.map( x => x * x )

Accepts a Function which...

  • 1st argument is the current array item
  • Return value is added to the new array

optional arguments 2 and 3 are less commonly used

map examples

[0, 1, 2, 3, 4, 5, 6, 7]
    .map((x) => 2**x)

// [1, 2, 4, 8, 16, 32, 64, 128]


[
    {w:10, h:20, d:10},
    {w:3, h:2, d:20},
    {w:4, h:1, d:400},
    {w:9000, h:3999, d:9191}
]
    .map( box => box.w * box.h * box.d)


// [2000, 120, 1600, 330793281000]
items.filter((x) => x%2===0)

The filter() method creates a new array with all elements that pass the test implemented by the provided function.

 

The new array will be the same size or smaller

filter

filter

Accepts a Function which...

  • 1st argument is the current array item
  • Return determines if the item stays:
      truthy=included, falsey=excluded

optional arguments 2 and 3 are less commonly used

items.filter( x => x%2===0 )
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    .filter(x => x % 2 === 0);

[
    "bacon cheeseburger", 
    "chicken sandwich", 
    "hamburger", 
    "bacon salad"
]
    .filter(x => /bacon/i.test(x) );

filter examples

items.reduce((acc, cur) => acc + cur, 0)

Applies a function against an accumulator and each element in the array to reduce it to a single value

 

Always results in a single item

reduce

the function accepts optional arguments 3 and 4, but they're less common

  • 1st argument is the accumulation of values that will eventually be the final results (required)
  • 2nd argument is the current item as the array is iterated through
  • Return value becomes the accumulation in the next iteration and then the final result

Accepts an initial accumulation value

items.reduce((acc, cur) => acc + cur, 0)

reduce

Accepts a Fuction which...

1

3

2

5

6

8

0

1

4

6

11

17

25

[1, 3, 2, 5, 6, 8].reduce(
    (acc, cur) => acc + cur,
    0
);

lab

Resources

Template Literals

Modern JavaScript

Template Literals

Template literals are string literals allowing embedded expressions. You can use multi-line strings and string interpolation features with them. They were called "template strings" in prior editions of the ES2015 / ES6 specification.

const price = 24.99;
const payments = 3;
const salesPitch = `Only ${payments} easy payments of $${price}!`

Note that the template literal is surrounded with back-ticks, not single quotes.

lab

Resources

Default Parameters

Modern JavaScript

Default Parameters

Default function parameters allow formal parameters to be initialized with default values if no value or undefined is passed.

/*
  A function "add" with default
  parameters which are used
  if no values are passed
*/
const add = (a=1, b=2) => {
  return a + b;
}

// call add with both parameters
console.log( add(3, 3) );

//call add with 1 missing parameter
console.log( add(3) );

//call add with no parameters
console.log( add() );

//call with one undefined parameter
console.log( add(undefined, 5) );

lab

Destructuring Assignment

Modern JavaScript

Destructuring Assignment

The destructuring assignment syntax is a JavaScript expression that makes it possible to extract data from arrays or objects into distinct variables.

const BigBird = { height: `8'2"`, color: '#f7f16d', age: 'timeless' };

const { color, height } = BigBird;
//color === '#f7f16d'
//height === `8'2"`

const colors = [ '#f00', '#0f0', '#00f' ];

const [ red, green ] = colors;
//red === '#f00'
//green === '#0f0'

const [,, blue] = colors;
//blue === '#00f'

"color", "height", "red", "green" and "blue" would all be variables and could be used just like if you declared

"const color = "#f7f16d;"

Destructuring Assignment in Parameters

Values from an object can be destructured when that object is passed into a function as a parameter.

const render = ({title, description}) => {
    console.log(title); // "..."
}
  
const bigObject = {
    ...
    title: '...',
    ...
    description: '...'
    ...
}
  
render(bigObject)

lab

Resources

Spread & Rest

Modern JavaScript

Array & Object Spread

The spread syntax allows an expression to be expanded in places where multiple arguments (for function calls) or multiple elements (for array literals) or multiple variables (for destructuring assignment) are expected.

const a = [ 1, 2, 3 ]
const flatArray = [ ...a, 4, 5 ];
//flatArray == [ 1, 2, 3, 4, 5 ]

const b = { a: 1, b: 2, c: 10, d: { a: 1 } };
const flatObject = { ...b, c: 3 };
//flatObject == { a: 1, b: 2, c: 3, d: { a:1 } };

Array & Object Rest

When used in a destructuring assignment, the rest operator assigns the remaining (the rest of) values to a variable.

const numbers = [ 1, 2, 3, 4, 5 ];
const [ one, two, ...threeFourFive ] = numbers;

// one == 1
// two == 2
// threeFourFive === [ 3, 4, 5 ];


const data = { a: 1, b: 2, c: 4, d: 5 };
const { a, b, ...rest } = data;

// a == 1
// b == 2
// rest == { c: 4, d: 5 }

Explain This?

(({ a, b, z = 100, ...others }) => {
   // a==1
   // b==2
   // z==100
   // others == { c: 3, d: 4, e: 5}
})({ a: 1, b: 2, c: 3, d: 4, e: 5 })

lab

Resources

Promises

Modern JavaScript

Promises

  • The Promise object is used for asynchronous computations.
  • A Promise represents a value which may be available now, or in the future, or never.
  • This is represented by three states: resolved, pending, or rejected.

Promises

Status Code
pending getData() ...
... working ...
resolved .then(res => ...) SUCCESS
or
rejected .catch(err => ...) ERROR
Our App Their Server

Promises

// mock functions
const displayHoorayMessage = ()=>{};
const queueWelcomeEmail = ()=>{};
const queueHandwrittenPostcard = ()=>{};
const doError = ()=>{};

// a function that returns a promise
signUpNewUser()
  .then(  displayHoorayMessage )
  .then(  queueWelcomeEmail )
  .catch( doError )
  .then(  queueHandwrittenPostcard )
  .catch( doError );
  • a resolved promise triggers the `.then` method
  • a rejected promise triggers the `.catch` method
  • the return value from `.then` and `.catch` is always a Promise
  • the return value from `.then` and `.catch` will be the function input of the next triggered `.then` or `.catch` callback

Promise.all

var p1 = new Promise( ( resolve, reject ) => { 
  setTimeout(resolve, 1000, 'one'); 
}); 

var p2 = new Promise( ( resolve, reject ) => { 
  setTimeout(resolve, 2000, 2); 
});

var p3 = new Promise( ( resolve, reject ) => {
  setTimeout(resolve, 3000, 'three');
});


Promise.all([ p1, p2, p3 ]).then( values => { 
  console.log(values); // ["one", 2, "three"] 
});

Promise.all returns a promise that resolves when all of the promises in the iterable argument have resolved, or rejects with the reason of the first failed promise.

Promise.resolve / Promise.reject

const results1 = Promise.resolve({ errors: false });
const results2 = Promise.reject({ errors: true });

any value can easily be converted into a promise

Async/Await

Async code that looks like synchronous code 

// A bunch of functions
const displayHoorayMessage = () => {};
const queueWelcomeEmail = () => {};
const queueHandwrittenPostcard = () => {};
const doError = () => {};

const processNewUser = async () => {
  try {
    const data1 = await signUpNewUser();
    const data2 = await queueWelcomeEmail(data1);
    return queueHandwrittenPostcard(data2);

  } catch (err) {
    doError(err);
  }
};

lab

Resources

Fetch

Modern JavaScript

Fetch

  • The Fetch API provides a JavaScript interface for accessing and manipulating parts of the HTTP pipeline, such as requests and responses.
  • It also provides a global fetch() method that provides an easy, logical way to fetch resources asynchronously across the network.
  • Fetch returns a promise. Results or errors are accessed using "then" and "catch" or "await."
fetch('http://service.example.org/json')
    .then(handleResponse)
    .catch(handleError)

Fetch

A configuration object can be optionally passed into the `fetch` call. This allows headers, and other options to be set on the request.

fetch('http://service.example.org/json', { 
    method: 'GET',
    headers: myHeaders,
    mode: 'cors',
    cache: 'default',
    credentials: 'same-origin'
})

headers & configuration

Fetch

  • Fetch returns a promise, and can be interacted with in the same way a promise is: using `then` and `catch`, or `await`.
  • In addition to the response body, the response object contains several helper functions and properties, such as ".json()" (converts response body to json), and ".ok" (checks the response status code)
fetch('http://service.example.org/json')
.then(res => res.ok ? res.json() : Promise.reject(res))
.then(res => {
  //do something with the response
})
.catch(err => {
  //do something with the error
});

working with the respone

Fetch

fetch('http://service.example.org/json')
  .then(res => res.ok ? res.json() : Promise.reject())
  .then(({prop1, prop2}) => ({ prop1, prop2 }))
  .then(res => {
    //do stuff
  })
  .catch(res => console.log(res));

trimming a response

Fetch

try {
  const res = await fetch("http://service.example.org/json");
  if (res.status === 401) {
    // The request is unauthorized
    redirectToLogin();
  } else if (res.ok) {
    return res.json();
  } else {
    throw Error(res.statusText);
  }
} catch (e) {
  displayError("Please try again");
}

acting on the status code

lab

Resources

Classes

Modern JavaScript

"Classes"

  • JavaScript classes are "syntactical sugar" over JavaScript's existing prototype-based inheritance.
  • The class syntax is not introducing a new classical-inheritance model to JavaScript.
  • JavaScript classes provide a simpler syntax to create objects and deal with inheritance.

"Classes"

class Shape {

  constructor(center) {
    this.center = center;
  }
  
}

const myShape = new Shape({ x: 0, y: 0 });

Extends

class Circle extends Shape {

  constructor(radius, center) {
    super(center);
    this.radius = radius;
  }
  
  area() {
    return Math.PI * (this.radius * this.radius);
  }
  
}

const myCircle = new Circle( 15, { x: 0, y: 0 });

myCircle.area(); // 706.8583470577034

Classes extend other classes, using prototypal inheritance. If a child class contains a constructor, it must also call the parent constructor, using the "super" function.

Class "Fields"

// before

class Shape {

  constructor() {
    this.name = "Shape";
    this.color = "Blue";
  }
  
}

An extension to JS classes (stage3) allows for instance properties 

// after

class Shape {
  name = "Shape";
  color = "Blue";
}

lab

Resources

The Future

Modern JavaScript

ReactJS

Part 2

What is React?

ReactJS

JS Library UI Library

  • Web (react-dom)
  • Native mobile (react-native)
  • Native desktop (proton-native)
  • TV (react-tv)
  • Virtual Reality (react-vr)
  • CLIs (react-blessed)
  • ...

Platform agnostic library (react)

Targets

Declarative

  • views reflect data
  • easily account for different states
  • more predictable code
  • easier to debug

Component Based

  • encapsulated components
  • compose to build complex UIs
  • components manage own state*

Virtual DOM

  • fast
  • declarative
  • future friendly

Many Ways to React

  • Page widgets
  • Browser-only SPA
  • Server-only rendered templating
  • Universal (aka "isomorphic") apps
  • With Redux
  • With Apollo
  • ...

Good Sized

react

react-dom

Thinking in Components

ReactJS

Separation of Concerns

JS

CSS

HTML

CSS

JS

HTML

in a page model

/page-1

/page-2

what does it look like when we want to share & reuse smaller components across multiple pages, apps & sites?

Separation of Concerns

JS

CSS

HTML

in a component model

Button, DatePicker, Modal, List, List-Item, Media

Button, DatePicker, Modal, List, List-Item, Media

Button, DatePicker, Modal, List, List-Item, Media

Separation of Concerns

JS

CSS

HTML

in a component model

Button

DatePicker

Modal

List

ListItem

Media

in this model, how a components fulfills its defined responsibility is an encapsulated, implementation detail

Best Practices: Redefined

Components IRL

Components IRL

Component driven design and development

  • Facilitates reusability within an application & between applications
  • Simplifies the mental model for developers when working on an isolated component → less errors

Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.

💯

What are Components?

reusable markup & style

is a component

Tile

<Tile
  title=""
  subTitle=""
  backgroundImageSrc=""
  href=""
/>

AsideSection

are components

reusable markup & style

Split

Layout

also a component

<Split ratio="1/3" gapSize="xs">
  <aside>I'm a distraction</aside>
  <main>I'm the main content</main>
</Split>

reusable behaviors

are components

<Network
  render={({ online }) =>
    <p>You are online: {online ? 'Yep' : 'Nope'}.</p>
  }
/>

Information

as components

<Fetch
  url={url}
  onData={handleData}
  onError={handleError}
/>

Units of Work

are components

"good components" do one thing, do it well, are reusable, and are composable

Best Practice

JSX

ReactJS

JSX

const name = "Tacos";
const when = "always";

const thingToRender = (
  <section>
    <p>What should we eat: {name}</p>
    <p>When should we them: {when}</p>
  </section>
);
  • open specification
  • XML-like syntax extension to JS
  • syntactic sugar that turns "tokens" into JS spec in a pre-processing stage
  • although parts of JSX may look like HTML, it is not.
  • in React, JSX turns into React.createElement

JSX

  • camelCase "props"
  • class ➡️ className
  • for ➡️ htmlFor
  • key when looping
  • defaultValue
  • always close tags
  • JS expressions

camelCase Props

<input onChange={console.log} />
<div className="hello">hi</div>

class ➡️ className

<label htmlFor="otherId">hi</label>

for ➡️ htmlFor

<ul>
  {[0, 1, 2].map( num =>
    <li key={num}>{num}</li>
  )}
</ul>

`key` (when looping)

<input defaultValue="hello" />

defaultValue

<input />
<br />
<hr />
<meta />

must close tags

<div>
  { Math.random() > 0.5 && <span>you win</span> }
</div>

<div>{ functionThatReturnsRenderable() }</div>

{ 5 > 3
  ? <strong>yes</strong>
  : <small>no</small>
}

{values.map(n => <strong>{n}</strong>)}

<date>
  {(new Date()).toLocaleString()}
</date>

Expressions

jsx

lab

  • https://facebook.github.io/jsx/

Resources

Components

ReactJS

A function which returns JSX is called a component. Component names must be capitalized.

//Create Component
const MyComponent = () => (
  <h1>A nice static component!</h1>
);

//Render the component
ReactDOM.render(
  <MyComponent />,
  document.getElementById("app")
);

A component could also include a function body

//Create Component
const MyComponent = () => {
   // do stuff before returning JSX element
   return (
      <h1>A nice static component!</h1>
   )
};

//Render the component
ReactDOM.render(
  <MyComponent />,
  document.getElementById("app")
);
  • The simplest way to create a component is to write a function (as you've already seen)
  • Generically, a function accepts inputs and produces output.
  • For a React component, we call those inputs "props".
//Create Component
const Greet = (props) => (
  <h1>Hello, {props.name}!</h1>
);
  • Typically, props are descructured for clarity and simplicity
//Create Component
const Greet = ({ name }) => (
  <h1>Hello, {name}!</h1>
);

Props can be:​

 

strings

 

//Render the component
ReactDOM.render(
  <Greet name="Alice" />,
  document.getElementById("app")
);

Props can be:​

 

​integers

 

//Render the component
ReactDOM.render(
  <Greet age={42} />,
  document.getElementById("app")
);

Props can be:​

 

​objects

 

//Render the component
ReactDOM.render(
  <Greet data={{ name: "Alice", gender: "Female"}} />
  document.getElementById("app")
);

Props can be:​

 

function reference

 

const callback = () => { console.log('Hello World') };

//Render the component
ReactDOM.render(
  <Greet onClick={callback} />,
  document.getElementById("app")
);

Props can be:​

 

arrays

 

const people = ["Albert", "Scott"];

//Render the component
ReactDOM.render(
  <Greet items={people} />,
  document.getElementById("app")
);

Props can also be spread:​​

const person = {name: "Alice", gender: "Female"};

//Render the component
ReactDOM.render(
  <Greet { ...person } />,
  document.getElementById("app")
);

// <Greet { ...person } /> === <Greet name="Alice" gender="Female" />

Props can be:​

 

🎇 You Name It! 🎇

 

as long as it's an expression

lab

Resources

Styling

ReactJS

Styling in React

  • Inline Styles
  • Global CSS
  • Import CSS
  • CSS Modules
  • CSS-in-JS

Styling in React

Inline Styles

Inline Styles use object literal notation in JSX

//Declare properties using camelCase
const myHeaderStyle = {
    paddingTop:'1em',
    fontFamily:'Palatino',
    backgroundColor:'aliceblue'
};

const MyHeader=()=>(
    <h3 style={myHeaderStyle}>
        Styling in React
    </h3>
);

Inline Styles

Pros

  • No support for pseudo selectors (e.g. :hover, :visited, etc.)
  • No support for Media queries
  • No support for animation
  • No single style source, difficult to maintain at scale
  • Styles scoped to component
  • Not dependent on global styles
  • 100% Reusable
  • Dynamic Values

Cons

Inline Styles

Styling in React

Global CSS

Don't do this (generally)

Using Global CSS

Due to 'class' being a reserved word, you need to use 'className'

//In a separate Style Sheet 
.header{
    padding-top : 1em;
    font-family : Palatino;
    background-color : aliceblue;
};

const MyHeader=()=>(
    <h3 className="header">
        Styling in React
    </h3>
);
  • Maintaining Global Style Sheets at scale
  • Cascading and specificity become less easy to predict
  • Components styles are dependent on a global style sheet, difficult to reuse component
  • Central point for Styles
  • Access to Pseudo Selectors, Media Queries, and Animations
  • Central point for Styles

Pros

Cons

Global CSS

Styling in React

Import CSS

Import CSS

import './my-header.css';

const MyHeader=()=>(
    <h3 className="header">
        Styling in React
    </h3>
);
/* my-header.css */
.header {
    font-family: "Comic Sans"
}

one "imported" .css file per component

  • Components are not dependent on a global styles sheet.
  • Component's styles come with it
  • Registers a static dependency
  • Requires a specific Build Configuration
  • Dev Team must implement a naming convention such as BEM to prevent naming collisions
  • Why not Automate naming conventions?

Pros

Cons

Import CSS

Styling in React

CSS Modules

CSS Modules

like import css but no name collisions

import styles from  './my-header.css';

const MyHeader=()=>(
    <h3 className={styles.header}>
        Styling in React
    </h3>
);
/* my-header.css */
.header {
    font-family: "Comic Sans"
}
  • CSS scoped to the Component
  • Class names are made unique for me
  • Requires a specific build tool configuration
  • All Classes are built into a single CSS file no matter if you need them or not
  • Components are not 100% portable

Pros

Cons

CSS Modules

Styling in React

"CSS-in-JS"

💪

CSS-in-JS

CSS is written in the JS and the CSS is injected into the styled tag in the head of the document

import styled from 'styled-components';

const Header = styled.h3`
    padding-top : 1em;
    font-family : Palatino;
    background-color : aliceblue;
`;

const MyHeader= () => (
    <Header>
        Styling in React
    </Header>
);
  • No specific build tool configuration needed
  • Class names are generated for me
  • Components are 100% portable
  • Values can be Dynamically Assigned
  • "Theme"ing available
  • Critical Path CSS
  • It's different*
  • Lose the potential benefit of a cached stylesheet*
  • Market churn
  • Class names are harder to know

Pros

Cons

"CSS-in-JS"

lab

Resources

UI Events

ReactJS

Pass functions instead of strings

<!-- The HTML Way -->

<button onclick="recordLike()">
    Click if you like!
</button>
// The React Way

<button onClick={this.recordLike}>
    Click if you like!
</button>

Differences

  • Have event handlers as React class methods
  • Allows for full encapsulation
class LikeButton extends React.Component {

    constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        console.log(this.props.thankYouMessage);
    }

    render () {
        return (
            <button onClick={this.handleClick}>
                Like
            </button>
        );
    }
}
class LikeButton extends React.Component {
    
    // Without Binding...
    //constructor(props) {
    //    super(props);
    //    this.handleClick = this.handleClick.bind(this);
    //}

    handleClick() {
        console.log(this.props.thankYouMessage);
        // ...Uncaught TypeError: Cannot read property
        // 'thankYouMessage' of undefined
    }

    render () {
        return (
            <button onClick={this.handleClick}>
                Like
            </button>
        );
    }
}

Gotcha: Bind member functions for the correct 'this'

class LikeButton extends React.Component {
    
    // No binding needed. `handleClick` is auto-bound
    // to LikeButton's `this`.
    handleClick = () =>
        console.log(this.props.thankYouMessage);

    render () {
        return (
            <button onClick={this.handleClick}>
                Like
            </button>
        );
    }
}

... Or just use the Class Field syntax

event.target

const ActionLink = () => {

  const handleClick = (event) => {
    // 'event' has lots of useful data
    console.log(event.target.href); // logs "#"
    console.log(event.target.nodeName) // logs "A"
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

Access the Event's Origin Element?

<!-- The HTML Way -->

<a href="#" onclick="console.log('The link was clicked.'); return false">
  Click me
</a>

Use e.preventDefault() instead of "return false;"

Preventing Default HTML Behavior

// The React Way

const ActionLink = () => {

  const handleClick = e => {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

lab

Resources

Components with State

ReactJS

The Counter

class Counter extends React.Component {
  state = {
    count: 0
  }

  handleReset = () =>
    this.setState({ count: 0 });

  handleIncrement = () => {
    this.setState(state => ({
      count: state.count + 1
    }))
  }

  render() {
    const { count } = this.state;
    return (
      <div>
        <strong>{count}</strong>
        <button onClick={this.handleReset}>reset</button>
        <button onClick={this.handleIncrement}>+</button>
      </div>
    )
  }
}

Remote State?

class Counter extends React.Component {
  state = {
    results: []
  }

  handleFetch = () => {
    fetch("http://example.org/something")
      .then( res => res.json() )
      .then( results => this.setState({ results }) )
  }

  render() {
    const { results } = this.state;
    return (
      <div>
        <button onClick={this.handleFetch}>Fetch!</button>
        {results.map(item => <Other {...item} />)}
      </div>
    )
  }
}

lab

Lifecycle Events

ReactJS

Each React component has several “lifecycle methods” that you can override to run code at particular times in the process.

  • Methods prefixed with will are called right before something happens.
  • Methods prefixed with did are called right after something happens.

These methods are called when an instance of a component is being created and inserted into the DOM:

  1. constructor()
  2. getDerivedStateFromProps()
  3. render()
  4. componentDidMount()

 

Mounting

An update can be caused by changes to props or state. These methods are called when a component is being re-rendered:

  1. getDerivedStateFromProps()
  2. shouldComponentUpdate()
  3. render()
  4. getSnapshotBeforeUpdate()
  5. componentDidUpdate()

Updating

This method is called when a component is being removed from the DOM:

  • componentWillUnmount()

 

 

Unmounting

React Lifecycle Methods Diagram

This lifecycle method is called when there is an error during rendering, in a lifecycle method, or in the constructor of any child component.

  • componentDidCatch()

 

 

Error Handling

These methods are being gradually phased out

  • componentWillMount()
  • componentWillReceiveProps()
  • componentWillUpdate()

 

 

Being Phased Out

  • UNSAFE_* aliases introduced in React 16.3
  • Deprecation messages will appear in a future 16.x release
  • Only the aliases will work in React 17+

 

 

Phase Out Plan

getInitialProps

import React, { Component } from "react";
import fetch from "isomorphic-unfetch";

const api = "https://api.github.com/repos/zeit/next.js"

class HomePage extends Component {
  static async getInitialProps() {
    const response = await fetch(api);
    const json = await response.json();
    return { stars: json.stargazers_count };
  }

  render() {
    return (
      <div>
        <p>Next.js has {this.props.stars} ⭐️</p>
      </div>
    );
  }
}

export default HomePage;
  • Added with Next.js (from Starter)
  • Runs on server first
  • Runs on client when navigating

lab

Resources

Forms in React

ReactJS

HTML Forms

HTML Forms typically manage their own state. For example, a text element contains it's own value (state). 

React Forms

React form elements can be either controlled or uncontrolled. 

Controlled Forms

The value of form elements are controlled via state.

class Form extends React.Component {
  state = {
    value: ""
  }

  handleChange = e =>
    this.setState({ value: e.target.value });

  render() {
    const { value } = this.state;
    return (
      <input onChange={this.handleChange} value={value} />
    );
  }
}

Prefer Controlled

This allows the conversion of user input into data of any shape and allows for friendlier user feedback and validation.

lab

Interacting with the DOM

ReactJS

On occasion you will need access to a DOM element

  • Managing focus, text selection, or media playback.
  • Triggering imperative animations.
  • Integrating with third-party DOM libraries.

`ref`

  • create a ref object with React.createRef()
  • add the ref attribute to the element
  • the DOM node is available at the `current` attribute
class AutoFocus extends React.Component {
  myRef = React.createRef();

  componentDidMount = () => {
    this.myRef.current.focus();
  }

  render() {
    return <input ref={this.myRef} />
  }
}

createRef() notes:

  • Use Class components
  • Use the syntax from the previous slide on React 16.3+

Callback ref's

  • considered the advanced syntax to ref's
  • works with older and newer React
class AutoFocus extends React.Component {
  handleRef = element => {
    this.textInput = element;
  };

  componentDidMount = () => {
    this.textInput.focus();
  }

  render() {
    return <input ref={this.handleRef} />
  }
}

string ref's

  • has issues
  • deprecated
  • convert these when you see them
class AutoFocus extends React.Component {
  ...

  render() {
    return <input ref="inputRef" />
  }

}

forwarding a`ref`

sometimes you'll need to pass a ref through to a child

const FancyButton = React.forwardRef((props, ref) => (
  <button ref={ref} className="FancyButton">
    {props.children}
  </button>
));

// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;

Smells

  • Avoid using refs for anything that can be done declaratively
  • Don't overuse refs
    refs is not your go-to tool to "make things happen"

lab

Resources

Sharing State

ReactJS

"Lifting State Up"

  • often, several components need to reflect the same changing data.
  • problem: who "owns" the state?

solution: lift state to closest, common ancestor

provide a single source of truth

lab

Resources

Render Props

ReactJS

Render Props

Component A takes a thing to render as a prop, does its work, and then renders said thing with results of its work.

<Network
  render={({ online }) =>
    <p>You are online: {online ? 'Yep' : 'Nope'}.</p>
  }
/>

Render Props

export default class Network extends Component {
  static defaultProps = {
    render: () => null,
    onChange: () => {}
  }

  state = {
    online: window.navigator.onLine
  }

  componentDidMount() {
    window.addEventListener("offline", this.handleChange)
    window.addEventListener("online", this.handleChange)
    this.props.onChange(this.state)
  }

  componentWillUnmount() {
    window.removeEventListener("offline", this.handleChange)
    window.removeEventListener("online", this.handleChange)
  }

  handleChange = () => {
    const online = window.navigator.onLine
    this.props.onChange({ online })
    this.setState({ online })
  }

  render() {
    return this.props.render(this.state)
  }
}

"Function-as-Child"

<Network>
  {({ online }) =>
    <p>You are online: {online ? 'Yep' : 'Nope'}.</p>
  }}
</Network>

sometimes: "Render-Callbacks"

lab

Resources

Rendering null

ReactJS

More than Markup

React is great at creating visual components, but that's just scratching the surface

<MediaQuery
  query="(min-width: 800px)"
  onChange={this.handleScreenChange}
/>

<LockScreen active />

<Hotkey
  combo="Ctrl+p"
  onHotkey={this.handleHotkey}
/>

lab

Higher Order Components

ReactJS

Higher Order Components

  • an advanced technique in React for reusing component logic
  • a higher-order component transforms a component into another component
  • use for "cross-cutting" concerns

Example

Many components need strings.

const ComponentA = ({strings}) => (
  <h1>{strings.welcomeMessage}</h1>
);


const ComponentB = ({strings}) => (
  <h1>{strings.goodByeMessage}</h1>
);

Example

A HOC that specializes in providing strings

const withStrings = Component => {
  const strings = magicallyGetStrings();
  return props => (
    <Component
      {...props}
      strings={strings}
    />
  );
}

More Complex Example

A HOC that provides fetch data

const withFetchData = url => Component => {
  return class extends React.Component {
    state = { data: null };

    componentDidMount() {
      fetch(url)
        .then( res => res.json() )
        .then( data => this.setState({ data }) )
    }

    render() {
      const { data } = this.state;
      return !data
        ? null
        : (
          <Component
            {...this.props}
            data={data}
          />
        );
    }
  }
}

lab

Composition

ReactJS

Composition

  • React has a powerful composition model
  • use composition instead of inheritance to share functionality
  • create complex UIs via composition of simple, single purpose components

Example

const Modal = ({ active, children, onCloseGesture, ...props }) => (
  <ModalMask {...props} active={active} onClick={onCloseGesture}>
    <Lock active={active} />
    <KeyDown keys={{ Escape: active && onCloseGesture }}>
      <Card onClick={capture} onKeyDown={capture}>
        {children}
      </Card>
    </KeyDown>
  </ModalMask>
);

explain this?

Lab

the final project is this lab 🙂

Data Management

Part 3

Start with

local state

getInitialProps

Review: Local State

import React, { Component } from "react";

class Counter extends Component {
  state = { value: 0 };

  increment = () => {
    this.setState(previousState => ({
      value: previousState.value + 1
    }));
  };

  render() {
    return (
      <div>
        {this.state.value}
        <button onClick={this.increment}>+</button>
      </div>
    );
  }
}
  • Comes with React to hold component state
  • May be all you need

Review: getInitialProps

import React, { Component } from "react";
import fetch from "isomorphic-unfetch";

const api = "https://api.github.com/repos/zeit/next.js"

class HomePage extends Component {
  static async getInitialProps() {
    const response = await fetch(api);
    const json = await response.json();
    return { stars: json.stargazers_count };
  }

  render() {
    return (
      <p>Next.js has {this.props.stars} ⭐️</p>
    );
  }
}
  • Comes with Next.js to initialize props with data
  • May be all you need

When to add a data management library

  • Persisting data across app loads
  • Optimizing performance of API requests
  • Caching data
  • Combining local + remote data
  • If GraphQL back-end
  • etc.

The Starter doesn't have one by default because not all apps need one

When you might want to add one

Redux

REST back-end

Apollo

GraphQL back-end

Which library?

  • These are the officially supported data management libraries
  • Apollo is generally a better option if you have or can create a GraphQL back-end
  • If you can't have a GraphQL back-end, you can use Redux instead

What is Redux?

Data Management

What's Redux?

  • Literally? Redux is a small JavaScript library that provides several helper methods for managing application state.
  • The bulk of "Redux" is actually just simple, functional JavaScript functions used in a certain paradigm.
  • Redux defines itself as a predictable state container for JavaScript apps.
  • Redux makes you think of your application as an initial state being modified by a sequential list of actions.

Safe Global State

3 Pillars of Redux

Data Management

1. Single Source of Truth

all Redux State is stored in a single object

const state = {
  books: {
    id1: {
      id: "id1",
      name: "Green Eggs and Ham"
    }
  },
  claps: {
    id1: 10
  } 
};

2. State is Read Only

no setters. changes are made by emitting actions describing what happened.

const action = {
  type: "BOOK_CLAP",
  payload: {
    id: "id1"
  }
}

3. Changes via Pure Functions

actions flow through reducers which optionally produce new state

const claps = ( state = {}, { type, payload } ) => {
  switch( type ) {
    case "BOOK_CLAP": {
      const { id } = payload;
      return {
        ...state,
        [id]: state[id] ? state[id] + 1 : 1
      }
    }
    default:
        return state
  }
} 
          

Redux Parts

Data Management

Redux Birds Eye

Action Object

const action = {
  type: "BOOK_CLAP",
  payload: {
    id: "id1"
  }
}
const createClap = (id) => ({
  type: "BOOK_CLAP",
  payload: { id }
});

Action Creator

Actions

type is required

Reducers

const claps = ( state = {}, { type, payload } ) => {
  switch( type ) {
    case "BOOK_CLAP": {
      const { id } = payload;
      return {
        ...state,
        [id]: state[id] ? state[id] + 1 : 1
      }
    }
    default:
        return state
  }
} 
          

always return new memory reference when change occurs

Selectors

const selectBookById = (state, id) => {
  const book = state.books[id];
  const claps = state.claps[id] || 0;

  return {
    ...book,
    claps
  }
}

   

optional but recommended helpers to grabbing parts from state

Redux Middleware

  • extends capability
  • logging
  • dev tools

Creating a Store

import { createStore, combineReducers } from "redux";
import { books, claps } from "./reducers";
import { selectBookById } from "./selectors";
import createClap from "./actions";

const rootReducer = combineReducers({ books, claps });

const store = createStore(rootReducer);

const unsubscribe = store.subscribe( () => {
  const state = store.getState();
  console.log( selectBookById(state, "id1") );
});

store.dispatch( createClap("id1") );

unsubscribe();

lab

React + Redux

Data Management

Redux

Notifies subscribers when its state changes

React

Changes the UI when props or state changes

Simple Integration

import React from "react";
import store "./app/store";

class ConnectedComponent extends React.Component {

  componentDidMount() {
    this.unsub = store.subscribe( () => {
      const state = store.getState();
      const stuffICareAbout = selectMyStuff(state);
      this.setState({stuffICareAbout});
    })
  }

  componentWillUnmount() {
    this.unsub();
  }

  // ...
}

aka naive 

React-Redux Binding

import { connect } from "react-redux";
import { createClap } from "./app/store/actions";
import { selectBooks } from "./app/store/selectors";
import BookView from "./app/components/BookView";

const mapStateToProps = state => ({ 
  books: selectBooks(state)  
});

const bindActionsToDispatch = { createClap };

const storeConnector = connect(
  mapStateToProps,
  bindActionsToDispatch
);

export default storeConnector( BookView );

the connect HoC

NextJS Redux Wrapper

// in one of your NextJS pages
import withRedux from "next-redux-wrapper";

import { initStore } from "./app/store/storeFactory";
import { createClap } from "./app/store/actions";
import { selectBooks } from "./app/store/selectors";

// Your page component...
const View = () => <div>I am app</div>;

const mapStateToProps = state => ({ 
  books: selectBooks(state)  
});

const bindActionsToDispatch = { createClap };

export default withRedux(
    initStore, 
    mapStateToProps,
    bindActionsToDispatch
)(View);

the withRedux HoC

Apollo

Data Management

What is Apollo?

Manages your data for you

*if you have a GraphQL back-end

Apollo is a lot simpler + faster than Redux

If possible, try to get or create a GraphQL back-end so you can use Apollo

Step 1

Set up GraphQL pieces

type Query {
  books(search: String): [Book!]
}

type Book {
  id: ID!
  volumeInfo: VolumeInfo!
}

type VolumeInfo {
  title: String!
  authors: [String!]
  imageLinks: ImageLinks
}

type ImageLinks {
  smallThumbnail: String
  thumbnail: String
}

Schema

{
  books {
    id
    volumeInfo {
      title
    }
  }
}

GraphQL Syntax

Can use any fields in schema

const api = "https://react-bootcamp-labs-api.app.lds.org";

const response = await fetch(api, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    query: `
      {
        books {
          id
          volumeInfo {
            title
          }
        }
      }
    `
  })
});

const json = await response.json();
console.log(json);

Get back JSON

{
  "books": [
    {
      "id": "1",
      "volumeInfo": {
        "title": "Bible"
      }
    },
    {
      "id": "2",
      "volumeInfo": {
        "title": "Book of Mormon"
      }
    }
    ...
  ]
}

GraphiQL

Step 2

Add presentation components

import React from "react";
import { H3 } from "@lds/eden-headings";

const Books = ({ books }) => (
  <section>
    {books.map(book => (
      <div key={book.id}>
        <H3>{book.volumeInfo.title}</H3>
      </div>
    ))}
  </section>
);

export default Books;

Step 3

Give presentation components data

Without Apollo

import React from "react";
import fetch from "isomorphic-unfetch";
import Books from "components/Books";

export default class SomePage extends React.Component {
  static async getInitialProps() {
    const response = await fetch(
      "https://react-bootcamp-labs-api.app.lds.org",
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          query: `
            graphql syntax here...
          `
        })
      }
    );

    const json = await response.json();
    return { books: json.data.books };
  }

  render() {
    console.log(this.props.books);

    return <Books books={this.props.books} />;
  }
}
{
  books {
    id
    volumeInfo {
      title
    }
  }
}
import gql from "graphql-tag";

const GET_BOOKS = gql`
  {
    books {
      id
      volumeInfo {
        title
      }
    }
  }
`;

With Apollo

import React from "react";
import { Query } from "react-apollo";
import Books from "components/Books";

const BooksWithData = () => (
  <Query query={graphql syntax here...}>
    {({ loading, error, data }) => {
      if (loading) return "Loading...";
      if (error) return "Error!";

      return <Books books={data.books} />;
    }}
  </Query>
);
import React from "react";
import gql from "graphql-tag";
import { Query } from "react-apollo";
import Books from "components/Books";

const GET_BOOKS = gql`
  {
    books {
      id
      volumeInfo {
        title
      }
    }
  }
`;

const BooksWithData = () => (
  <Query query={GET_BOOKS}>
    {({ loading, error, data }) => {
      if (loading) return "Loading...";
      if (error) return "Error!";

      return <Books books={data.books} />;
    }}
  </Query>
);

Step 4

Add Apollo root setup

Client Only

Next.js

Used in lab for simplicity

Recommended for real projects using React Starter

What's inside Apollo render props?

  • Apollo takes care of the request cycle from start to finish
  • Tracks loading and error states for you
  • No boilerplate to write
  • Don't need to worry about transforming and caching data
  • All you do is describe the data your component needs Apollo does the heavy lifting 💪
  • Advanced features like optimistic UI, refetching, and pagination are all accessible from render props if you need them

Why Apollo?

  • Works with any JavaScript front-end
    • React, Angular, Vue, Polymer, Ember, vanilla etc.
  • Declarative
    • Delete a lot of unnecessary data management code
    • Can still access imperative pieces if needed
  • Query + Mutation components handle:
    • Fetch + cache data
    • Handle data loading + error state
    • Change data
    • Update UI from data changes
  • Addons for common problems

See apollographql.com for more

  • Mutations (changing data)
  • Authentication
  • Subscriptions
  • Pagination
  • Optimistic UI
  • Global error handling
  • Logging
  • Advanced manual control over Apollo defaults
  • apollo-link-* for common problems
    • Auto deduplicates queries
    • Auto retries on network or server errors
    • Auto batch requests
    • Auto mocking data from schema
    • etc.

GraphQL + Apollo === Declarative

  • The schema is the data contract
  • List data requirements where used
  • Smart defaults
  • Advanced control if needed

lab

Next JS

Final Project

lab

Reading List

Reading List

  • User searches for a title

  • Fetches data from Google Book API

  • Displays Book Data in Tile

  • User can mark a book as read, or delete it altogether

Components

  • <App />

  • <Heading />

  • <BookSearch />

  • <Stack />

  • <Tile />

  • <QuickForm />

  • <Icon />

Suggested approach

  • Build presentation components using Storybook
  • Compose your components into a page
  • Wire up the API call for searching

 

don't worry about Redux/Apollo unless you finish early

Google Books API

React Training

By Bruce Campbell

React Training

  • 2,248