busy developer's workshop: react - neward & associates · format lecture/lab format –i...
TRANSCRIPT
Busy Developer's Workshop:React
Ted Neward
Neward & Associates
http://www.tedneward.com | [email protected]
Before we begin...
Please make sure you have...– installed Git
https://git-scm.org/downloads/
– installed NodeJS
https://nodejs.org/en/
– installed VisualStudio Code
https://code.visualstudio.com/download
– these slides are available at
http://www.newardassociates.com/slides/Workshops/React.pdf
– the GitHub project to follow is at
https://www.github.com/tedneward/ReactWorkshop
Objectives
In this workshop, we're going to...– make sure your JavaScript is up to speed
TypeScript is also usable, but out of scope for us
– go over the basics of React
•components•JSX syntax•state, props and fields
– briefly discuss some advanced concepts
•higher-order components•context•hooks
Format
How we gonna do this?
Format
Lecture/lab format– I will lecture for a bit
– You will code/debug/explore for a bit
I will try to wander the room and help where I can
– Lather, rinse, repeat
– Turn in your evals and you are free!
Format
"The Rules"– Pairing and interaction
absolutely encouraged
– Googling/Binging/looking stuff up online
absolutely encouraged
– Leaving the room during a "lab"
absolutely encouraged (if necessary)
– Jumping ahead
encouraged, but I can only help for "this" step
Format
"Requests"– set the phone/volume/etc to vibrate
basically, don't disturb the neighbors
– ask questions!
if you're thinking it, so are other people
– ... but let's keep the focus to the here and now
there's a lot to cover, so I need to keep us focused
Format
Ready?– Any questions?
– If not, here we go....
– Last chance... abandon hope, all ye who remain...
ECMAScript 2016
Language core
ECMAScript Overview
Overview
ECMAScript has ...– ... an imperative C-family-of-languages syntax
– ... a classless object system
– ... functional objects
– ... loosely-typed type system
– ... a metaobject protocol
– ... a garbage collected object heap
– ... and a few bad design decisions/legacy
ECMAScript Basics
Getting the tiny bits out of the way
Basics
Starting points– Whitespace: space, tabs, CRLF, etc
mostly irrelevantline terminator (";") mostly optional
– Comments: // (end-of-line) and /* */ (multi-line)
– Identifiers/Names: [A-Za-z][A-Za-z0-9...]
Basics
Built-in types understood– Booleans: true, false
other values devolve into "truthy" and "falsy"
– Numbers: always a 64-bit floating-point type, NaN
•Binary literals
0b111110111 (503 decimal)
•Octal literals
0o767 (503 decimal)
– Strings: 16-bit Unicode
– Objects: bag of name-value pairs
– Functions: invokable code
– null, undefined
Basics
Template Strings– "Backticked" strings
•backtick character is now a new string literal delimiter•backticked-strings are multiline-aware
– Strings now support an "interpolation" form
•similar to Python, Perl, Ruby, etc•use ${name}-qualified identifiers•combines with backticks very nicely
Basics
Template Strings
// String interpolationvar name = "Bob", time = Date.now();console.log(`Hello ${name}, how are you ${time}?`);
Basics
Variables– signified with keyword, followed by legal name
– any variable used without keyword-based declaration is global
this is generally considered badbe particularly careful in "for" loops
– variables are typeless
but the things they point to are typedjust not very strongly; coercion is always possible
Basics
var vs let vs const– var is a mutable variable declaration
function-scoped
– Let is a mutable variable declaration
block-scoped
– Const is an immutable variable declaration
block-scoped
Basics
var, let and const
function f() {w = 12; // global! bad!{
let x;{
// okay, block scoped nameconst x = "sneaky";// error, const//x = "foo";
}// error, already declared in block//let x = "inner";var y = "Fred";z = 12;
}}
Basics
Arrays– untyped collections of values/variables in indexed order
– instantiated with []
– accessed via [], zero-indexed
Basics
Array use
let strings = ["This", "is", "an", "array","of", 5, "strings"
];console.log(strings[0]); // Prints "This"console.log(strings[5]); // Prints "5"
Basics
Destructuring Assignment– "pick out" parts of an object and assign to values
– common in a number of functional languages
– used with both objects and arrays (positional assignment)
– can also be used in function parameters
Basics
Destructuring Assignment
var [a, b, c, d] = ['hello', ', ', 'world', 'ignored'];console.log(a + b + c);
// Some interesting objects we're working withvar pt = {x: 123, y: 444};var rect = {topLeft: {x: 1, y: 2}, bottomRight: {x: 3, y: 4}};
var {x, y} = pt; // unpack the pointvar {topLeft: {x: x1, y: y1}, bottomRight: {x: x2, y: y2}} = rect;
console.log(x, y);console.log(x1, y1, x2, y2); // 1,2,3,4
// Can be used in parameter positionfunction g({name: x}) {
console.log(x);}g({name: 5});
Basics
Operators– operator set similar to that from C-family langs
but there are some subtle and dangerous differences!
– + - * / % : mathematical operators
– <= >= != < > : comparison operators
– === !== : equality/inequality operators
ES also supports == and !=, but they attempt conversion
– && || ! : logical operators
– typeof : returns type of object
object, function, undefined, Number, String, ...
Basics
What's truthy? What's falsy?
0 == '''' == '0'false == '0'false == nullnull == undefinedfalse == undefined
Basics
What's truthy? What's falsy?
0 == '' (true)'' == '0' (false)false == '0' (true)false == null (false)null == undefined (true)false == undefined (false)
ECMAScript Flow Control
if, while, for, etc
Flow Control
if: boolean decision-branching– "truthy" values execute block
– "falsy" values skip block
•"else" block executed, or...•"else if" condition examined, or ...•we just drop to the next statement
Flow Control
while: boolean decision-looping– "truthy" values execute the loop body
– "falsy" values drop to the next statement
– "do-while" just puts the conditional at the bottom of the loop body
Flow Control
for: condition-terminating loop– "traditional" for (initialize/conditional/increment)
– "for-in" iteration
iterates over the properties/elements in the collection/object
– "for-of" iteration
iterates over the keys in the collection/object
Flow Control
Iterators and for-of– Custom iteration a la CLR IEnumerable or Java Iterable
– Use "for ... of" similar to "for ... in"
– No actual collections required, just an object that follows the Iterable protocol
•object must have a next() method•return from the next() method must have a "done" and "value" field
Flow Control
Iterators and for-of
for (let element of [1, 2, 3]) {console.log(element);
}
let fibonacci = {[Symbol.iterator]() {
let pre = 0, cur = 1;return {
next() {[pre, cur] = [cur, pre + cur];return { done: false, value: cur }
}}
}}
Flow Control
Iterators over generic array
function iterateElements(array) {return {
[Symbol.iterator]: function() {var index = 0;var current;return {
next: function() {if (index < array.length) {
current = array[index++];return { done:false, value: current };
}return { done:true, value: current };
}};
}};
}
let numbers = [1, 2, 3, 4, 5, 6];for (let n of iterateElements(numbers)) {
console.log(n);}
ECMAScript Functions
Functions
Functions syntax– composition: 4 parts
"function"name (optional)parameter set (0 - n named arguments)statement block
– function can appear anywhere an expression is expected
top-level, as object members, nested, and so onnested functions have access to their enclosing scope
Functions
Function "interesting" notes– functions are objects, too
– two implicit arguments to every function invocation
'this': reference whose contents vary with invocation pattern'arguments': array of arguments passed in
– unlike other languages, functions don't enforce arity
missing arguments are undefined, extras are in 'arguments'
Functions
Functions
function addIt(first, second) {return first + second
}println(addIt(1, 2))
var addItAgain = function(first, second) {return first + second
}println(addItAgain(1,2))
println(function(first, second) {return first + second
}(1, 2))
var add = function() {var result = 0;for (var i = 0; i<arguments.length; i++)
result += arguments[i]return result
}println(add(1, 2, 3, 4, 5))
Functions
Function invocation patterns– Function Invocation: function is not an object member
"this" is bound to the global object
– Method Invocation: function is an object member
"this" is bound to object on which function is being invoked
– Apply Invocation: function is invoked explicitly via apply()
"this" is bound to first argument in parameters list
– Constructor Invocation: function is used as a constructor
new object created with hidden link to function's prototype"this" is bound to newly-created object
Functions
Default, rest and spread parameters– set default values (for when no value is passed)
– "rest" parameters are an array capturing all remaining arguments
– "spread" parameters "spread" an array into multiple parameter slots
Functions
Default parameters
// Defaultsfunction slice(list, indexA = 0, indexB = list.length) {
// ...}
Functions
Rest parameters
// Rest paramsfunction push(array, ...items) {
items.forEach(function(item) {array.push(item);
});}var a = [];push(a, "1", "2", "3", "4", "5");console.log(a);
Functions
Spread parameters
function add(x, y) {return x + y;
}
var numbers = [4, 38];console.log(add(...numbers)); // 42
// Also works for array literalsvar a = [1];var b = [2, 3, 4];var c = [6, 7];var d = [0, ...a, ...b, 5, ...c];console.log(d);
Functions
Functions are objects– as such, have methods defined on them for manipulating
functions
defined in Function prototype
– bind()
provides a new "this" reference for method invocation
– apply()
use of the () operator on an object attempts to invoke apply()
Functions
Closures– referenced values remain around as long as function does
the function "closes over" the reference variable (hence the name)
– the actual link isn't explicit or discoverable
this provides opportunities to "hide" members from the object on which a function operates, to avoid pollution
Functions
Closures
var myObj = function() {var value = 0;return {
increment: function(inc) {value += typeof inc === 'number' ? inc : 1;
},getValue: function() {
return value;}
};}();myObj.increment()println(myObj.getValue())for (var m in myObj)
println(m + " = " + myObj[m])myObj.value = 10println(myObj.getValue())
Functions
Higher-order functions– passing functions into functions
– examples: Array.map, .filter, etc
Functions
Closures
var evens = [2, 4, 6, 8];var odds = evens.map(function (v) { return v + 1 });var nums = evens.map(function (v, idx) { return v + idx } );var pairs = evens.map(function (v) { return {even: v, odd: v + 1} } );
Functions
Arrow functions– lexical "this" binding
– shorter syntax
intended for anonymous/lambda usage
Functions
Arrow function syntax
// Lexical thisvar bob = {
_name: "Bob",_friends: [],printFriends() {
this._friends.forEach(f =>console.log(this._name + " knows " + f));
}}bob._friends.push("Fred");bob.printFriends();
Functions
Arrow function syntax
// Expression bodiesvar evens = [2, 4, 6, 8];var odds = evens.map(v => v + 1);var nums = evens.map((v, idx) => v + idx);var pairs = evens.map(v => ({even: v, odd: v + 1}));console.log(odds);console.log(nums);console.log(pairs);
Functions
Arrow function syntax
// Statement bodiesvar fives = [];nums.forEach(v => {
if (v % 5 === 0)fives.push(v);
});console.log(fives);
ECMAScript classes and objects
Prototypical object-oriented programming
Objects
Objects are essentially a bag of name-value pairs– values can be either data or function values
– classless system: no concept of "class", just "objects"
– "open" objects: members can be added/removed
– members accessed through refinement operators (. [])
– use [] to access illegal identifier names (as keys)
Objects
Objects
var speaker = {'firstName' : 'Ted','lastName' : 'Neward',sayHello : function() {
println("Hello!")},sayHowdy : function() {
println("Howdy!")}
}println(speaker.firstName)println(speaker["lastName"])speaker.sayHowdy()speaker["sayHello"]()
for (var m in speaker) {println(m + "=" + speaker[m])
}
Objects
Object prototypes– objects always have a "prototype" object
– prototype is always in scope when resolving names
– this creates a "prototype chain" of names
– we can control the prototype used at construction ...
... but the syntax for doing so in ECMAScript is... complicated.
– instead, monkey-patch Object and add a create() method
Objects
Objects and prototypes
var empty = { }for (var m in empty) {
println(m + "=" + empty[m])}println(empty.toString())
Objects
Monkey-patching
if (typeof Object.create !== 'function') {Object.create = function(proto) {
var F = function() {};F.prototype = proto;return new F();
};}
var base = {sayHowdy : function() { println("Howdy") }
}var derived = Object.create(base)for (var m in derived) {
println(m + "=" + derived[m])}derived.sayHowdy()
Objects
Monkey-patching
// Method to add a method to any particular prototypeFunction.prototype.method = function (name, func) {
if (!this.prototype[name]) {this.prototype[name] = func;
}return this;
};// add an 'roundOff' method to NumberNumber.method('roundOff', function() {
return Math[this < 0 ? 'ceil' : 'floor'](this);});println((5.2).roundOff())println((-12.2).roundOff())
Objects
Classes– constructors
– instance and static members
static fields/methods are on the prototype object
– fields (properties)
– methods
– simple sugar over prototype-based OO
•still prototype-based inheritance•"super" calls to prototype
Objects
Classes
class Monster extends Character {constructor(x, y, name) {
super(x, y);this.name = name;this.health_ = 100;
}
attack(character) {super.attack(character);
}
get isAlive() { return this.health_ > 0; }get health() { return this.health_; }set health(value) {
if (value < 0) throw new Error('Health must be non-negative.');this.health_ = value;
}}
Objects
Enhanced Object literals– bring class declarations and object literals closer together
•set the prototype at construction•shorthand syntax for same-name assignments•defining methods•making super calls•compute property names with (runtime) expressions
ECMAScript 6 Classes
Object initializer shorthand
var handler = function() {console.log("Handled!");
};var theProtoObj = {
name: "Proto"};var obj = {
// __proto____proto__: theProtoObj,// Shorthand for 'handler: handler'handler,// MethodstoString() {
// Super callsreturn "d "; // + super.toString();
},// Computed (dynamic) property names[ 'prop_' + (() => 42)() ]: 42
};obj.handler();console.log(obj.toString());console.log(obj.prop_42);
ECMAScript modules
Organizing/structuring code
Modules
ECMAScript wants to allow for structuring of code– ... without specifying concrete (loading) details
– modules are essentially at file scope
Modules
Imports and exports– to make a symbol available from the module, "export" it
– to use a symbol exported from another file, "import" it
•multiple imports from the same file use a {} syntax to pull each directly•you cannot blanket-import all exported symbols from a module (by design)•you can import all exported symbols from a module by giving them a top-level name
Modules
Module usage
// lib/math.jsexport function sum(x, y) {
return x + y;}export var pi = 3.141593;
// app.jsimport * as math from "lib/math";alert("2π = " + math.sum(math.pi, math.pi));
// otherApp.jsimport {sum, pi} from "lib/math";alert("2π = " + sum(pi, pi));
NPM
Node Package Manager
npm
Node doesn't have everything "out of the box"– in fact, there is less in the box than expected
– fortunately, the box comes with a "grow the box" tool
npm
npm: Node Package Manager– command-line tool to install/manage node packages
– full list of packages is at http://search.npmjs.org/
WARNING: this is a huge list, some are good, some are crapBetter to know what you want before hunting for it
– Must be online to use npm!
because it pulls package sources from npmjs.org, so...
npm
npm installs to a directory– node_modules/
– generally, treat this as opaque
•toying with this can open a huge Pandora's box•if it gets wedged, just nuke it and re-install
– "global" installs will install to a user/system-wide directory
varies with platform
npm
npm commands:– all take a "-g" parameter to mean "global"
globally will (sometimes) require admin rights
– npm help {command}: man page on {command}
– often two forms:
•long name: "remove"•unix-style alias: "rm"
npm
npm init– creates npm manifest (package.json)
•describes dependencies•describes dev-only dependencies•describes entry point script•describes other scripts•... and so on
– JSON format
package.json
package.json
{"name": "sample-app","version": "1.0.0","description": "Sample application","main": "app.js","dependencies": {
"debug": "^2.2.0"},"devDependencies": {},"scripts": {
"test": "node --debug app.js"},"author": "Ted Neward","license": "ISC"
}
npm
npm install– npm install {package}
•downloads package to node_modules•if "--save" or "--save-dev" is provided, saves to manifest
– npm install
•examines manifest•downloads all packages locally•typically used to set up code for execution
– there may be binaries/scripts installed
use "npm bin" to find out where
npm
npm ls / npm list / npm ll– list the packages installed
g will list all global packages installed
– ll will list in a "long" format
includes some additional info about the package
npm
npm upgrade / npm up– upgrade app dependencies to latest version
– subject to manifest's versioning declarations
•"^" dependencies will be upgraded to whatever's latest•"~" dependencies will only upgrade to latest minor version•so "^1.1.1" will upgrade to 1.1.2, 1.2.0, or 2.0•but "~1.1.1" will upgrade to 1.1.2, but not 1.2.0 or 2.0
– use "--save" to record new versions in manifest
npm
npm remove / npm rm– removes the package from the application
– use "--save" to record removal in the manifest
npm
npm {command}– "npm run" or "npm test" or ...
– directly corresponds to the "scripts" section in manifest
– commonly used as a lightweight build tool
•"npm test" to launch automated tests•"npm run" to kick off application execution
npm
npm explore {package}– open a shell in node_modules for {package}
– avoids having to resolve by hand
useful for package debugging
– not often used by non-package developers
npm
Note: if npm fails– npm will display output to console window
– it will attempt to carry on
– it will write all output to "npm-debug.log" in current directory
Modules
Some interesting modules to explore:– Express: Web framework
– Sequelize: RDBMS access module
– Passport: User-authentication
– More--see https://npmjs.org
Break time!
Take a few to brace yourself
React Basics
Getting started
React
What is it?
React
A user-interface platform for HTML/Web– "just the V in MVC"
– leverages custom features of Javascript
– component-oriented
UI inside the component code (JSX)
– open-source
– built by Facebook
Getting Started with React
From zero to Hello World!
Getting Started
Prerequisites– NodeJS
– text editor of some form
VisualStudio Code is popular
– a Web browser
Getting Started
Install the prerequisites
npm install -g create-react-app
Getting Started
Scaffold out
create-react-app hello-world
Getting Started
Starting
npm start
Bundle the app into static files
npm run build
Run the rest runner
npm test
Getting Started
index.js
import React from 'react';import ReactDOM from 'react-dom';import './index.css';import App from './App';import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));registerServiceWorker();
Getting Started
App.js
import React, { Component } from 'react';import logo from './logo.svg';import './App.css';
class App extends Component {render() {
return (<div className="App">
<header className="App-header"><img src={logo} className="App-logo" alt="logo" /><h1 className="App-title">Welcome to React</h1>
</header><p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p></div>
);}
}
export default App;
Getting Started
JSX, ES2015, and React– React uses JSX as its language of choice
•JSX is an ECMAScript + XML variant•transpiled by Babel (among others)•helps to unify code and markup
– not required
anecdotally, most React devs seem to be using it
– bear in mind, JSX is not HTML
•JSX is transformed into ECMAScript•... which is then executed in the browser•there may be some oddities in behavior because of this•consider examining the transpiled source
Getting Started
Installing Babel
npm install -g babel-cli
Installing some nice Babel pre-set configuration
npm install -g babel-preset-es2015 babel-preset-react
Set up Babel presets in .babelrc
{"presets": [ "es2015", "react" ]
}
Getting Started
Hello world– Scaffolded app doesn't say much
– the Gods of Computer Science demand their recognition!
Getting Started
Hello world– a single text message saying, "Hello" to the world
– component-oriented: we need a "hello" component
could abstract this further, to be a "message" or "text" component, if desired
Getting Started
Hello component– properties: the message to display
•we could go crazy and add others, like style, font, etc•this will not change after initialization/instantiation
– behavior: none
– events: none
Getting Started
Hello
import React from 'react';
export class Hello extends React.Component {render() {
return (<div>{this.props.message}</div>)}
}
Getting Started
In App.js, import Hello
import { Hello } from './Hello';
In App.js, use it
<p className="App-intro">To get started, edit <code>src/App.js</code> and save to
reload.</p><Hello message="Hello, world!" />
Hands-On Time!
Part 1: React scaffolding!
HOT Pt 1
Part 1: React scaffolding– create new React app (rjoke)
– make sure it runs (both the tests and the app)
– build, import and use the Joke class
React Basics
Going into what React is and does
React Concepts
What you must know
Concepts
Declarative (over imperative)– React wants to tap into the declarative paradigm
say "what" instead of "how"
– heavily inspired by functional programming
which is often said to be synonymous with declarative(although it's possible to be declarative without being functional)
Concepts
Elements– "the smallest building blocks of React apps"
– immutable objects
– represent a DOM node
– can (and usually do) have multiple children
– contain only the information necessary to represent the interface
Concepts
Components– components are not objects
•or, at least, not strictly objects•React holds that functions can be objects too
– components are standalone units of state, behavior and notification
think objects without implementation inheritance
– reuse is through composition
not inheritance
Concepts
Unified– in other words, everything related to a component should
live inside that component
•code•UI rendering instructions•state/properties
– this flies in the face of "split" frameworks (like Angular or Vue)
Concepts
Rendering– each React component will "render" its UI on demand
– think of this as a function:
f(S) -> UI (where S is "state")
– this is how React will manage immutable UI controls
this can also help optimize UI refreshes
– Facebook encapsulated this into Flux
"a unidirectional flow of data"
Concepts
Virtual DOM– React builds a hidden "copy" of the DOM
– then compares the virtual DOM to the real one
– much more efficient than rendering directly to the browser DOM
– this also simplifies testing
JSX
React's expression syntax
JSX
JSX is entirely syntactic sugar– Not required
– wrapper around React.createElement()
– not a formal language; just something React recognizes and transpiles
not to be confused with E4X from a decade ago
JSX
JSX has to be able to determine what is React, what is HTML– React elements must be transpiled
– HTML elements must be passed through to the browser
– therefore, React types must be first-letter-capitalized
– MUST return a single-rooted (as in XML) tree
JSX
WRONG
import React from 'react';
// Wrong! This is a component and should have been capitalized:function hello(props) {
// Correct! This use of <div> is legitimate because // div is a valid HTML tag:return <div>Hello {props.toWhat}</div>;
}
function HelloWorld() {// Wrong! React thinks <hello /> is an HTML tag // because it's not capitalized:return <hello toWhat="World" />;
}
JSX
Corrected
import React from 'react';
// Correct! This is a component and should be capitalized:function Hello(props) {
// Correct! This use of <div> is legitimate because // div is a valid HTML tag:return <div>Hello {props.toWhat}</div>;
}
function HelloWorld() {// Correct! React knows <Hello /> is a component // because it's capitalized.return <Hello toWhat="World" />;
}
JSX
JSX props expressions– any JS expression can be passed as a prop inside of {}
– statements (if, for, etc) are not expressions
to use these, capture their results into a local var
JSX
"Spread attributes" can be quite helpful– use "..." to pass the whole props object to child
components
– pick specific props to consume and pass the rest via spread operator
JSX
These two components are equivalent
function UseGreeting() {return <Greeting firstName="Ben" lastName="Hector" />;
}
function UseGreetingUsingSpread() {const props = {firstName: 'Ben', lastName: 'Hector'};return <Greeting {...props} />;
}
JSX
Extracting one prop, passing the rest
const Button = props => {const { kind, ...other } = props;const className =
kind === "primary" ? "PrimaryButton" : "SecondaryButton";return <button className={className} {...other} />;
};
const App = () => {return (
<div><Button kind="primary" onClick={() => console.log("clicked!")}>
Hello World!</Button>
</div>);
};
JSX
Choose the element type at runtime– assign the type to a variable
– use that variable in the React element type expression
JSX
Photo or Video?
import React from 'react';import { PhotoStory, VideoStory } from './stories';
const components = {photo: PhotoStory,video: VideoStory
};
function Story(props) {// JSX type can be a capitalized variable.const SpecificStory = components[props.storyType];return <SpecificStory story={props.story} />;
}
JSX
Child elements are passed in a special prop: props.children– bools, null and undefined are ignored; they don't render
– these can be string literals (string appears in props.children)
strings are trimmed and concatenated
– these can also be any ECMAScript expression
such as an array of objects
– can be a mix of HTML and React components
HTML will just be strings intermixed among objects
JSX
Children (React elements)
<MyContainer><MyFirstComponent /><MySecondComponent />
</MyContainer>
Children (String literals)
<MyComponent>Hello world!</MyComponent>
JSX
Conditional child rendering
<div>{showHeader && <Header />}<Content />
</div>
<div>{props.messages.length > 0 &&
<MessageList messages={props.messages} />}
</div>
JSX
Expressions as children
function Item(props) {return <li>{props.message}</li>;
}
function TodoList() {const todos = ['finish doc', 'submit pr', 'nag dan to review'];return (
<ul>{todos.map((message) => <Item key={message} message={message} />)}
</ul>);
}
JSX
Functions as children
// Calls the children callback numTimes to produce a repeated componentfunction Repeat(props) {
let items = [];for (let i = 0; i < props.numTimes; i++) {
items.push(props.children(i));}return <div>{items}</div>;
}
function ListOfTenThings() {return (
<Repeat numTimes={10}>{(index) => <div key={index}>This is item
#{index} in the list</div>}</Repeat>
);}
React Components
The building blocks of React
Components
React components– represent a union of state, behavior and notifications
– practically, a method ("render") to describe the DOM elements to be built
– divide neatly into "stateless" and "stateful"
meaning, "does the component preserve internal state"
– explicitly differentiate between "state" and "data parameters" ("props")
•state changes over time•props are used to customize the component at creation
Components
Components are designed for composibility– components will (frequently) have children (components)
– these children will often do some/much of the heavy UI lifting
•but with some customization and/or connection by the parent•children are accessible via this.props.children property
Components
A sample "breakdown" of the Twitter profile page
<HomePage><Avatar src={} ... /><UserInfo user={} ... /><Counts tweets={} following={} ... /><Tweets list={} ... />
</HomePage>
Components
Three main ways to create a React component– Stateless function components
– extend React.Component
– React.createClass()
createClass() is unofficially deprecated
Components
Stateless function components– function itself represents the rendering logic
in other words, the function is assumed to be the "render" function
– optionally takes a "props" parameter for passing in values for use
– cannot have any internal state
– no lifecycle methods
Components
Pure function taking no parameters
var HelloMsg = () => <h1>Hello, React!</h1>
Pure function taking parameters ("props")
var ClickableImage = props => (<a href={props.href}>
<img src={props.src} /></a>
);
Components
Extending React.Component– uses ES 6 class syntax
– properties are passed in to constructor (to "this.props")
– state is stored directly in constructor (to "this.state")
– all functions are defined using "normal" function syntax
Components
class HelloMsg extends React.Component {render() {
return <h1>Hello, React!</h1>}
}
class ClickableImage extends React.Component {constructor(props) {
super(props);// This constructor will trigger a warning about being unnecessary
}render() {
return (<a href={this.props.href}>
<img src={this.props.src} /></a>
);}
}
Components
Props vs state vs fields– "props" are React's passed-in parameter set from the
component instantiation/use
these should/will never change
– "state" is the internal state that reflects on the component's UI
this changes over time
– "fields" are anything that doesn't fit in the above two categories
fields will usually be interim calculations, cache, etc
– most React use will center around props and state (not fields)
Component Properties
Working with component properties more effectively
Component Properties
ECMAScript is an untyped language– strings, numbers, booleans, they're all just objects
– ECMAScript will quite happily accept anything
– React will offer up no warnings or errors when given the wrong type
string instead of a number, for example
Component Properties
A NumbersList component (function style)
const NumbersList = props => (<div>
{props.list.map(number =><div key={number} className={`highlight-${number === props.x}`}>
{number}</div>
)}</div>
)
Using it (incorrectly!)
<NumbersList list={[a, b, c]} x="42" />
This fails (silently)
Component Properties
React allows us to define "property types"– React will verify and flag when expectations are violated
– "propType" property on component
– array of React.PropType constants
– React will validate properties (by name) against propType definitions
and warn in browser console when violations are detected
Component Properties
React.PropTypes– array
– bool
– number
– object
– string
– func
Component Properties
React.PropTypes– node: anything that can be rendered
– element: a React element
– instanceof(SomeClass)
– oneOf([...]): for enumerated values
– oneOfType([...]): list of acceptable types
– arrayOf(type)
– objectOf(type): object with property values of a certain type
Component Properties
Customizations– by default, all props are optional
•flip to required with chained isRequired property•any any PropTypes value
– can also write a two-argument function
params: props (array of properties), propName
Component Properties
Adding propTypes to NumbersList
NumbersList.propTypes = {x: React.PropTypes.number
}
Component Properties
NumbersList defined as class
class NumbersList extends React.Component { static propTypes = {
x: React.PropTypes.number};
}
Component Properties
Default values for properties– "defaultProps" array contains default values for properties
– these are only used when no values are passed in
Componet Properties
An AlertBox component
const AlertBox = props => (<div className="alert alert-{props.level}">
{props.message}</div> );
AlertBox.defaultProps = {level: "danger"
};
Hands-On Time!
Part 2: React components!
HOT Pt 2
Part 2: Some simple components– create a Header and Footer component for the app's
chrome
– create a Joke component around a Joke object
– create a Jokelist component to display all the Jokes
More React Basics
Getting more sophisticated
React State Management
Understanding state in React
State Management
Three things to know about state– Do not modify state directly
always use setState()
– State updates may be asynchronous
React may batch them up into a single update for performance
– State updates are merged
the object passed to setState is merged with (not replacing) the current state
State Management
Countdown timer (as a class)
export class Timer extends React.Component {constructor(props) {
super(props);this.state = { counter: this.props.initialSeconds };
}
componentDidMount() {let currentCounter;this.timerId = setInterval(() => {
currentCounter = this.state.counter;if (currentCounter === 1) {
clearInterval(this.timerId);}this.setState({ counter: currentCounter - 1 });
}, 1000);}
render() { return (<div>{this.state.counter}</div>); }};
State Management
"The Data Flows Down"– neither parent nor child components should know if a
component is stateful or -less
– they shouldn't care if it is a function or class
– state is encapsulated--inaccessible to any component other than its owner
– any state is always owned by some specific component
– any data or UI derived from that state can only affect components "below" it
State Management
Some components will be state containers– they will hold state and use sub-components for display
– conditional rendering selects between which sub-components
– isn't strictly necessary but does embrace composition well
State Management
Display components
function UserGreeting(props) {return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {return <h1>Please sign up.</h1>;
}
State Management
State container component
export function Greeting(props) {const isLoggedIn = props.isLoggedIn;if (isLoggedIn) {
return <UserGreeting />;}return <GuestGreeting />;
}
State Management
Remember updates can be asynchronous– this means setState() calls may not have "this" available
– setState() calls may also be batched or merged
– if new state depends on old state, use callback version of setState to reference previous state
State Management
Incorrect implementation
incrementCount() {// Note: this will *not* work as intended.this.setState({count: this.state.count + 1});
}
handleSomething() {// Let's say `this.state.count` starts at 0.this.incrementCount();this.incrementCount();this.incrementCount();// When React re-renders the component, `this.state.count` will be 1,
not 3}
State Management
Incorrect implementation
incrementCount() {this.setState((prevState) => {
// Important: read `prevState` instead of `this.state` when updating.return {count: prevState.count + 1}
});}
handleSomething() {// Let's say `this.state.count` starts at 0.this.incrementCount();this.incrementCount();this.incrementCount();// At this line, this.state.count = 0// When React re-renders, this.state.count = 3
}
Events
Responding to changes in the universe
Events
Two kinds of events in the universe– React lifecycle events
– "External" events
Events
React lifecycle events– notifications about the component
•it is being initialized•it is being "mounted"•... and so on
– to receive, implement a method of the right name on the component
React will invoke it when appropriate
Events
React lifecycle events– componentWillReceiveProps:
component is about to receive new props array
– componentWill/DidMount:
fired before/after the component instance writes to the browser
– shouldComponentUpdate:
component needs to update to new state; return true to update, false to remain un-updated
– componentWill/DidUpdate:
fired when component receives new props or new state
– componentWill/DidUnmount:
fired before/after the component instance is removed from the browser
Events
A Clock component
export class Clock extends React.Component {constructor(props) {
super(props);this.state = {date: new Date()};
}componentDidMount() {
this.timerID = setInterval(() => this.tick(), 1000);}componentWillUnmount() {
clearInterval(this.timerID);}tick() {
this.setState({date: new Date()});}render() {
return (<div><h2>It is {this.state.date.toLocaleTimeString()}.</h2></div>);
}};
Events
React can respond to browser events– define a method/function on the class
will need to re-bind "this" in the class, usually the constructor
– for the HTML element, use an expression that describes the function to invoke
arrow function, reference to defined method, etc
– React normalizes all browser events into a "SyntheticEvent"
W3C cross-browser standard; underlying native event is available on the synthetic event as "nativeEvent"
– browser event is passed to event function as first parameter
usually called "e" or "event"
Events
Most common "external" events– Keyboard events
onKeyDown, onKeyPress, onKeyUp
– Focus events
onFocus, onBlur
– Form events
onChange, onInput, onSubmit
– Touch events
onTouchStart, onTouchEnd, onTouchMove, onTouchCancel
– Mouse events
onClick, onDrag, onMouseOver, onMouseOut, etc
– also see clipboard events, wheel events, media events, and more
Events
ToggleButton
export class Toggle extends React.Component {constructor(props) {
super(props);this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callbackthis.handleClick = this.handleClick.bind(this);
}handleClick() {
this.setState(prevState => ({ isToggleOn: !prevState.isToggleOn }));
}render() {
return (<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}</button>
);}
}
Events
Sometimes we want to pass additional parameters– inside a loop it is common to want to pass an extra
parameter to an event handler
id of the row item or some equivalent
– two approaches work equally well:
•define an arrow function that takes the event parameter; invokes your method with additional parameter(s)•bind the event-handling function with the additional parameters directly
Events
Define an arrow function
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
Re-bind the event-handling function
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
Input
User input (forms) in React
Input
React allows standard HTML forms– for those cases where the standard HTML behavior "just
works"
– frequently, however, additional processing is desired/necessary
Input
Controlled components– in HTML, form elements usually hold their own state
– in React, mutable state is captured in components
– so let's capture the state in React as the "source of truth"
•which means updating it via React's setState() mechanism•and re-displaying it in each render() cycle
– NOTE: sometimes we will want to prevent default handling of an HTML event
such as form submission
Input
NameForm: render() logic
render() {return (
<form onSubmit={this.handleSubmit}><label>
Name:<input type="text" value={this.state.value}
onChange={this.handleChange} /></label><input type="submit" value="Submit" />
</form>);
}}
Input
NameForm: event-handling
export class NameForm extends React.Component {constructor(props) {
super(props);this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {this.setState({value: event.target.value});
}
handleSubmit(event) {alert('A name was submitted: ' + this.state.value);event.preventDefault(); // Form submission will load a new page
normally}
React
HTML considerations– input/text: reference the text with ".value"
– textarea: same as input/text
– select: choose which option with a "value" attribute on the select tag
track choice selection using onChange
– use "name" attribute to discern between UI controls in an event-handler
Hands-On Time!
Part 3: Events and State
HOT Pt 3
Part 3: Adding event-handling and input– hide the punchline
– create a VoteCounter component to track upvotes of Jokes
or anything else; there's nothing Joke-specific about upvoting
– creating new Jokes
or editing old ones, if you so desire
More React
Beyond the basics
React Context
Passing state down
Context
Passing props down through a tree– each component has to participate
– this can be error-prone and problematic
– some components may not know to participate
Context
React solves for this with contexts– allows us to pass a value deep into the tree
– without explicitly threading it through each component
– creates implicit dependencies on context objects; use with care
Context
Using contexts– create a context with React.createContext(default_value);
– create a point of context use with Context.Provider and pass a value
– read from the context type to get the "nearest" (hierarchically) context value
Context
Create the context (and related data)
export const themes = {light: {
foreground: '#000000',background: '#eeeeee',
},dark: {
foreground: '#ffffff',background: '#222222',
},};
export const ThemeContext = React.createContext(themes.dark // default value
);
Context
Use the Context.Provider (pt 1)
import ThemedButton from './themed-button';
// An intermediate component that uses a ThemedButton// without having to know or pass theme from its parentfunction Toolbar(props) {
return (<ThemedButton onClick={props.changeTheme}>
Change Theme</ThemedButton>
);}
Context
Use the Context.Provider (pt 2)
import {ThemeContext, themes} from './theme-context';
class App extends React.Component {constructor(props) {
super(props);this.state = {
theme: themes.light,};
this.toggleTheme = () => {this.setState(state => ({
theme:state.theme === themes.dark
? themes.light: themes.dark,
}));};
}// . . .
Context
Use the Context.Provider (pt 3)
// . . .render() {
// The ThemedButton button inside the ThemeProvider// uses the theme from state while the one outside uses// the default dark themereturn (
<Page><ThemeContext.Provider value={this.state.theme}>
<Toolbar changeTheme={this.toggleTheme} /></ThemeContext.Provider><Section>
<ThemedButton /></Section>
</Page>);
}}
Context
Read the current Context value
import {ThemeContext} from './theme-context';
class ThemedButton extends React.Component {render() {
let props = this.props;let theme = this.context;return (
<button{...props}style={{backgroundColor: theme.background}}
/>);
}}ThemedButton.contextType = ThemeContext;
export default ThemedButton;
React Hooks
Functions over classes
Hooks
Hooks are a new way of writing React– completely opt-in
– 100% backwards-compatible
– available as of v16.8.0 of React
Hooks
An example of Hooks in action
import React, { useState } from 'react';
function Example() {// Declare a new state variable, which we'll call "count"const [count, setCount] = useState(0);
return (<div>
<p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>
Click me</button>
</div>);
}
Hooks
Hooks ...– are functors that let you "hook into" React state and
lifecycle features from functions
– can be used repeatedly inside of a given function
– don't work inside of classes!
– come "out of the box" and can also be custom-created
Hooks
Hook rules– don't call hooks inside loops, conditions, or nested
functions
– only call hooks from React functions (not from "plain vanilla" JS functions)
Hooks
State hook– simplifies/allows manipulation of state in a React function
component
– import 'useState' from 'react' to use
– call useState() to create a state "tuple"
•pass the initial value for the state•returns a two-element array: the state variable, and the mutator function
– the state variable can be used directly for display
– the mutator function takes the new state to set
Hooks
The state hook in action
import React, { useState } from 'react';
function Example() {// Declare a new state variable, which we'll call "count"const [count, setCount] = useState(0);
return (<div>
<p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>
Click me</button>
</div>);
}
Hooks
Effect hook– perform side effects in function components
data fetching, setting up a subscription, manually changing the DOM
– import 'useEffect' from 'react'
– in essence, useEffect() fires after each and every render()
– if we need to "clean up" when the component unmounts, return a function
this function will be run when the component cleans up (unmounts, goes away, etc)
– there are ways to optimize effects if necessary
Hooks
Effect (adding to the previous example)
function Example() {const [count, setCount] = useState(0);
useEffect(() => {// Update the document title using the browser APIdocument.title = `You clicked ${count} times`;
});
return (<div>
<p>You clicked {count} times</p><button onClick={() => setCount(count + 1)}>
Click me</button>
</div>);
}
Hooks
Custom hooks– JavaScript function
– ... whose name starts with "use"
– ... and that may call other hooks
(but don't get crazy with creating new ones until you understand the existing ones really well)
Hooks
Hook takeaways– this will not remove classes from React
– hooks don't replace anything you know of React
– hooks are essentially an AOP attempt by the React team
and how successful they will be in the long term remains to be seen
React Higher-Order Components
Building components out of components
Higher-Order Components
React faces some problems all UI libraries face:– How do we put logging into all our components?
– How do we put data acquisition/release in all our components?
– How do we put easy debugging hints into all our components?
Higher-Order Components
Traditional design has no good answer here– inheritance can't solve the problem
these behaviors need to be implemented in each particular location
– procedural only takes us so far
we can capture some of the behavior into a function but it still needs to be called
– this problem has a name: "cross-cutting concerns"
Higher-Order Components
Aspect-Oriented Programming (AOP) provided a solution– cross-cutting behavior was captured in "aspects"
– aspects described where they would be "woven" (join points)
– aspects could inject behavior before, after or around code
– aspect-weavers would perform all the magic
– horribly complicated
Higher-Order Components
Functional programming provides a different answer– functions can be composed via partial application
– a function that is only partially applied (called) returns a function
not an error
Higher-Order Components
Partial application of an add function
// Haskell-ish examplelet add = function(l, r) { return l + r; };add(1, 2) ==> 3add(1) ==> ???
Higher-Order Components
Partial application of an add function
// Haskell-ish examplelet add = function(l, r) { return l + r; };add(1, 2) ==> 3let increment = add(1)increment(3) ==> 4increment(4) ==> 5
Higher-Order Components
React wants to compose components much the same way– take a component...
– ... pass it into another component...
– ... and that component "decorates" or "composes" the original
Higher-Order Components
Consider: Props logging– we want to log each time React components get props
– React.Component has the componentsWillReceiveProps() lifecycle method
– just write the props out in that method
in every single component that we might want to log
Higher-Order Components
Consider: Props logging– create a higher-order component that "wraps" a passed-in
component
– the HOC defers all code to the passed-in component
– ... except for the componentsWillReceiveProps() method
wherein it logs the props received
Higher-Order Components
Props logging component
function logProps(WrappedComponent) {return class extends React.Component {
componentWillReceiveProps(nextProps) {console.log('Current props: ', this.props);console.log('Next props: ', nextProps);
}render() {
// Wraps the input component in a container, without mutating it. Good!
return <WrappedComponent {...this.props} />;}
}}
Summary
Wrapping up (for now)
Summary
React is...– a highly-opinionated framework
– a highly-productive framework
– growing in popularity
– a very reasonable choice for your web v.Next applications
Summary
Where to go from here?– Redux
this is a common way to do state management across the app
– React Router
for "swapping" components in and out of the UI
– React Native
for building native(-ish) mobile apps using React metaphor
Credentials
Who is this guy?– Director, Platform Strategy -- Quicken Loans
– Principal -- Neward & Associates
– Author
Professional F# 2.0 (w/Erickson, et al; Wrox, 2010)Effective Enterprise Java (Addison-Wesley, 2004)SSCLI Essentials (w/Stutz, et al; OReilly, 2003)Server-Based Java Programming (Manning, 2000)
– Blog: http://blogs.tedneward.com
– Twitter: @tedneward
– For more, see http://www.newardassociates.com