ReactJS for Programmers by Dr. David Rodenas
Origin
2014 FB about Web 3 https://youtu.be/nYkdrAPrdcw?t=10m20s
FB about MVC 4 “MVC works pretty well for small applications… but it doesn’t make room for new features.” “Flux is a single direction data flow, that avoids all the arrows going on all directions what makes really hard to understand the system.”
FB about Chat 5 “Let’s see a real good example: FB chat” “How we get to the point, so we were annoying our users so much the just they wanted us to fix chat?” “The problems here were: • The code has no structure • It was very imperative, that makes it fragile • It loose a lot of the original intend behind it, its hard to tell what it tries [to do] • Add more features only gets this code larger • We had our most annoying chat bug happen over and over • We were always fixing some pretty good edge case, the whole system was fragile • … • This code becomes more fragile with the time. • No member of the team wanted to touch it, they wanted to jump to any other bug.”
FB about Rendering 6 “Imperative Rendering” “If renders all each time the screen flickers” “We wanted always render all, no matter what” “Here is where React comes in.”
FB & Virtual DOM 7
ReactJS
Prerequisite 9 Install React Developer Tools
Basics
Hello World <!-- hello-world.html --> <div id="root"></div> // hello-world.js const virtualDom = React.createElement( 'h1', null, 'Hello, world!' ); ReactDOM.render( virtualDom, document.getElementById('root') ); 11
Hello World <!-- hello-world.html --> <div id="root"></div> // hello-world.jsx const virtualDom = <h1>Hello, world!</h1>; ReactDOM.render( virtualDom, document.getElementById('root') ); 12
Hello World <!-- hello-world.html --> <div id="root"></div> // hello-world.jsx ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('root') ); 13 https://facebook.github.io/react/docs/hello-world.html
Hello World // This is translated by Babel to... ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('root') ); // ...to this ReactDOM.render(React.createElement( 'h1', null, 'Hello, world!' ), document.getElementById('root')); 14 https://babeljs.io/repl
• ReactJS only updates what changes • Although everything is regenerated every time • Open countdown example at codepen with chrome
 http://codepen.io/drpicox/pen/yMKWGN • Inspect page • You can see that only the span is updated
 (although this crude code changes everything) Do not fear change 15
JSX https://facebook.github.io/react/docs/introducing-jsx.html
JSX // Just sugar syntax const element = <h1>Hello, world!</h1>; // It is just one object const element = React.createElement( "h1", null, "Hello, world!" ); // one object that you can manipulate as object 17
JSX // May I? console.log(<h1>Hello!</h1>); console.log(<h1>Hello!</h1>.toString()); console.log([<h1>Hello!</h1>]); console.log({hello: <h1>Hello!</h1>}); console.log((() => <h1>Hello!</h1>)()); const salute = (what) => <h1>{what}!</h1>; console.log(salute('Hello')); 18
JSX // Have multiple elements (but one wrapper) const element = <h1><span>Hello</span>, world!</h1>; // Generates const element = React.createElement( "h1", null, React.createElement( "span", null, "Hello" ), ", world!" ); 19
JSX // It can be multiline (be careful using return) const element = <h1> Hello, world! </h1>; 20
JSX // It can be multiline (better) const element = <h1> Hello, world! </h1>; 21
JSX // It can be multiline (recommended) const element = ( <h1> Hello, world! </h1> ); 22
JSX - Interpolation // Interpolate any JS expression const name = 'bob hoskings'; const element = ( <h1> Hello, {name}! </h1> ); // It is just one argument more const element = React.createElement( 'h1', null, 'Hello, ', name, '!' ); 23
JSX - Interpolation // Interpolate any JS expression const user = {name:'bob', lastName:'hoskings'}; const element = ( <h1> Hello, {user.name + ' ' + user.lastName}! </h1> ); // It is still just one argument more const element = React.createElement( 'h1', null, 'Hello, ', user.name + ' ' + user.lastName, '!' ); 24
JSX - Interpolation // any expression, including JSX const element = <h1>Hello, {<span>world</span>}!</h1> // It is still just one argument more const element = React.createElement( "h1", null, "Hello, ", React.createElement( "span", null, "world" ), "!" ); 25
JSX - Properties // Add attributes const element = <div tabIndex="0">...</div>; // Is transformed into a property object (alias props) const element = React.createElement( "div", { tabIndex: "0" }, "..." ); 26
JSX - Properties // Add computed attributes const element = <img src={user.imageUrl} />; // Is transformed into a property object (alias props) const element = React.createElement( "img", { src: user.imageUrl }, ); 27
JSX - Properties // Set variable attributes const options = { autoplay: true, muted: true, }; const element = <video src="wow.mp4" {...options} />; // Are merged into attribute const element = React.createElement( "video", { src: "wow.mp4", ...options }, ); 28
JSX - Properties // Set variable attributes const options = { autoplay: true, muted: true, }; const element = <video src="wow.mp4" {...options} />; // Are merged into attribute const element = React.createElement( "video", { src: "wow.mp4", autoplay: true, muted: true }, ); 29
Properties vs Attributes <!-- html uses attributes --> <img id="hello" src="hello.jpg"> <!-- javascript uses properties --> const helloImg = document.getElementById('hello'); console.log(helloImg.src); 30
Properties vs Attributes <!-- html uses attributes --> <h1 id="hello" class="big">Hello</h1> <!-- javascript uses properties --> const helloH1 = document.getElementById('hello'); console.log(helloH1.className); 31
JSX - Properties // Because bad IE8 habits: className instead of class const element = <h1 className="big">Hello</h1>; // Is rendered as real html attribute: <h1 class="big">Hello</h1> 32
JSX - Cross-site-scripting // It is just a string const title = response.veryMaliciousInputString; // This is save const element = <h1>{title}</h1>; 33
JSX - XML // Is XML-like const element = ( <div> Hello <br/> World </div> ); // Elements must be terminated const element = ( <div> Hello <br> World </div> ); 34
Rendering Elements https://facebook.github.io/react/docs/rendering-elements.html
Rendering Elements // JSX elements are just memory objects const element = <h1>Hello, world</h1>; 36
Rendering Elements // Given an html DOM element <div id="root"></div> // they can be rendered inside DOM const domElement = document.getElementById('root'); const reactElement = <h1>Hello, world</h1>; ReactDOM.render( reactElement, /* into */ domElement ); 37
Rendering Elements // As many times as you want // react only updates changes const domElement = document.getElementById('root'); let n = 0; setInterval(() => { ReactDOM.render(<h1>{n += 1}</h1>, domElement); }, 1000); 38
Components and Props https://facebook.github.io/react/docs/components-and- props.html
Components // Can be defined as functions const HelloWorld = () => { return <h1>Hello, world</h1>; }; export default HelloWorld; 40
Components // Can be defined as functions const HelloWorld = () => { return <h1>Hello, world</h1>; }; export default HelloWorld; // Can be defined as classes export default class HelloWorld extends React.Component { render() { return <h1>Hello, world</h1>; } } 41
Component and properties const element = <Welcome name="Dave" />; 42
Component and properties const element = <Welcome name="Dave" />; // Remember: it is like write const element = React.createElement( Welcome, { name: "Dave" }, // are called props ); 43
Component and properties const element = <Welcome name="Dave" />; // This is how you define the Component to use props function Welcome(props) { return ( <h1>Hello {props.name}!</h1> ); } 44
Component and properties const element = <Welcome name="Dave" />; // This is how you define the Component to use props class Welcome extends React.Component { constructor(props) { super(props); } render() { return ( <h1>Hello {this.rops.name}!</h1> ); } } 45
Composing Components function App() { return ( <div> <Welcome name="Alice" /> </div> ); } 46
Composing Components function App() { return ( <div> <Welcome name="Alice" /> <Welcome name="Bob" /> <Welcome name="Dave" /> </div> ); } 47
Refactoring Components function Component(props) { return ( <div className="Comment"> <div className="UserInfo"> <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); } 48
Refactoring Components function Avatar(props) { return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); } 49 <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name} />
Refactoring Components function Component(props) { return ( <div className="Comment"> <div className="UserInfo"> <Avatar user={props.author} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); } 50
Refactoring Components function Component(props) { return ( <div className="Comment"> <div className="UserInfo"> <Avatar user={props.author} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); } 51
Refactoring Components function UserInfo(props) { return ( <div className="UserInfo"> <Avatar user={props.user} /> <div className="UserInfo-name"> {props.user.name} </div> </div> ); } 52
Refactoring Components function Component(props) { return ( <div className="Comment"> <UserInfo user={props.author} /> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); } 53
Props are Read-Only // Render must be read-only function MissbehavingComponent(props) { return <h1>{props.n += 1}</h1>; } 54
State and Lifecycle https://facebook.github.io/react/docs/state-and-lifecycle.html
Function to Class Component function Clock(props) { return ( <div> <h1>Hello, world!</h1> <h2>It is {props.time}.</h2> </div> ); } 56
Function to Class Component class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.time}.</h2> </div> ); } } 57
Function to Class Component class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.time}.</h2> </div> ); } } 58
Setup State class Clock extends React.Component { constructor(props) { super(props); this.state = {time: Date.now()}; } render() { ... } } 59
Setup State class Clock extends React.Component { constructor(props) { ... } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.time}.</h2> </div> ); } } 60
Lifecycle Methods class Clock extends React.Component { constructor(props) { ... } componentDidMount() { /* TODO */ } componentWillUnmount() { /* TODO */ } render() { ... } } 61
Lifecycle Methods class Clock extends React.Component { constructor(props) { ... } componentDidMount() { this.timerId = setInterval( () => this.setState({time: Date.now()}), 1000 ); } componentWillUnmount() { /* TODO */ } render() { ... } } 62
Lifecycle Methods class Clock extends React.Component { constructor(props) { ... } componentDidMount() { ... } componentWillUnmount() { clearInterval(this.timerId); } render() { ... } } 63
setState Asynchronous // Wrong! setState({counter: this.state.counter + 1}); // Correct setState((prevState, props) => ({ counter: prevState.counter + 1 }); 64
States are Merged constructor() { this.state = {posts: [], users: []}; } componentDidMount() { getPosts((posts) => this.setState({posts})); getUsers((users) => this.setState({users})); } 65
State Isolated function App() { return ( <div> <Clock /> <Clock /> <Clock /> </div> ); } 66
Handling Events https://facebook.github.io/react/docs/handling-events.html
onEvent={fn} function ButtonLogHello() { function handleClick() { console.log('The button is clicked'); } return ( <button onClick={handleClick}> click me </button> ); } 68
onEvent={fn} // It received the event as parameter function ALogHello() { function handleClick(ev) { ev.preventDefault(); console.log('The link is clicked'); } return ( <a href="#" onClick={handleClick}> click me </a> ); } 69
onEvent={fn} // It can inline functions (are expressions) function DivLogHello() { return ( <button onClick={() => console.log('oh')}> click me </button> ); } 70
Toggle Example class Toggle extends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; } ... 71
Toggle Example class Toggle extends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; } handleClick() { ... } ... 72
Toggle Example class Toggle extends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; // This binding is necessary by `this` this.handleClick = this.handleClick.bind(this); } handleClick() { ... } ... 73
Toggle Example class Toggle extends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; // Fat arrow function preserves `this` this.handleClick = () => { ... }; } ... 74
Toggle Example class Toggle extends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; } // Stage 2 alternative (recommended official) handleClick = () => { ... } ... 75
Toggle Example class Toggle extends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; } handleClick = () => { … } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'on' : 'off'} </button> ); } } 76
Toggle Example class Toggle extends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; } handleClick = () => { // Warning, wrong implementation this.setState({ isToggleOn: !this.state.isToggleOn }); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'on' : 'off'} </button> ); } } 77
Toggle Example class Toggle extends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; } handleClick = () => { this.setState((prevState) => ({ isToggleOn: !prevState.isToggleOn, }); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'on' : 'off'} </button> ); } } 78
Conditional Rendering https://facebook.github.io/react/docs/conditional- rendering.html
Conditional Rendering function UserGreeting(props) { return <h1>Welcome back!</h1>; } function GuestGreeting(props) { return <h1>Please sign up.</h1>; } 80
By return function Greeting(props) { const isLoggedIn = props.isLoggedIn; if (isLoggedIn) { return <UserGreeting />; } return <GuestGreeting />; } ReactDOM.render( // Try changing to isLoggedIn={true}: <Greeting isLoggedIn={false} />, document.getElementById('root') ); 81
By variable render() { const isLoggedIn = this.state.isLoggedIn; let button = null; if (isLoggedIn) { button = <LogoutButton onClick={this.handleLogoutClick} / >; } else { button = <LoginButton onClick={this.handleLoginClick} />; } return ( <div> <Greeting isLoggedIn={isLoggedIn} /> {button} </div> ); } 82
By && expression function Mailbox(props) { const unreadMessages = props.unreadMessages; return ( <div> <h1>Hello!</h1> {unreadMessages.length > 0 && <h2> You have {unreadMessages.length} unread messages. </h2> } </div> ); } 83
By ?: expression render() { const isLoggedIn = this.state.isLoggedIn; return ( <div> {isLoggedIn ? ( <LogoutButton onClick={this.handleLogoutClick} /> ) : ( <LoginButton onClick={this.handleLoginClick} /> )} </div> ); } 84
Prevent Render function WarningBanner(props) { if (!props.warn) { return null; } return ( <div className="warning"> Warning! </div> ); } 85
Lists and Keys https://facebook.github.io/react/docs/lists-and-keys.html
Remember... const numbers = [1, 2, 3, 4, 5]; const doubled = numbers.map((n) => n * 2); console.log(doubled); // [2, 4, 6, 8, 10] 87
Render Multiple // Render a reactElements array const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((number) => <li>{number}</li> ); ReactDOM.render( <ul>{listItems}</ul>, document.getElementById('root') ); 88
Render Multiple // Refactor into component function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => <li>{number}</li> ); return ( <ul>{listItems}</ul> ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={numbers} />, document.getElementById('root') ); 89
Key function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => <li key={number.toString()}> {number} </li> ); return ( <ul>{listItems}</ul> ); } 90
Key - index const todoItems = todos.map((todo, index) => // Only do this if items have no stable IDs <li key={index}> {todo.text} </li> ); 91
Key - Refactor Component function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => <li key={number.toString()}> {number} </li> ); return ( <ul>{listItems}</ul> ); } 92
Key - Refactor Component function ListItem(props) { return ( // Wrong! should not be here <li key="props.number.toString()"> {props.number} </li> ); } 93
Key - Refactor Component function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // Correct: key must remain with map <ListItem key={number.toString()} number={number} /> {number} ); return ( <ul>{listItems}</ul> ); } 94
Key - Uniqueness // Cannot repeat keys in the same list, but... function DoubleList(props) { return ( <div> <NumberList numbers={props.numbers} /> <NumberList numbers={props.numbers} /> <NumberList numbers={props.numbers} /> </div> ); } 95
.map inside JSX function NumberList(props) { const numbers = props.numbers; return ( <ul> {numbers.map((number) => <ListItem key={number.toString()} value={number} /> )} </ul> ); } 96
Forms https://facebook.github.io/react/docs/forms.html
Controlled Components class NameForm extends React.Component { constructor() { ... } handleChange = (event) => { this.setState({value: event.target.value}); } handleSubmit = (event) => { ... } render() { return ( <form onSubmit={this.handleSubmit}> <input type="text" value={this.state.value} onChange={this.handleChange} /> </form> ); } } 98
Controlled Components class NameForm extends React.Component { constructor(props) { super(props); this.state = {value: ''}; } handleChange = (event) => { ... } handleSubmit = (event) => { ... } render() { ... } } 99
Controlled Components class NameForm extends React.Component { constructor() { ... } handleChange = (event) => { this.setState({ value: event.target.value.toLowerCase() }); } handleSubmit = (event) => { ... } render() { ... } } 100
Textarea <textarea value={this.state.value} onChange={this.handleChange} /> 101
Select <select value={this.state.value} onChange={this.handleChange}> <option value="grapefruit">Grapefruit</option> <option value="lime">Lime</option> <option value="coconut">Coconut</option> <option value="mango">Mango</option> </select> 102
Multiple Inputs <input name="isGoing" type="checkbox" checked={this.state.isGoing} onChange={this.handleInputChange} /> <input name="numberOfGuests" type="number" value={this.state.numberOfGuests} onChange={this.handleInputChange} /> 103
Multiple Inputs handleInputChange(event) { const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name; this.setState({ [name]: value }); } 104
Synchronized States https://facebook.github.io/react/docs/lifting-state-up.html
Synchronizing - Consumer // We want an <input> to generate new values for function BoilingVerdict(props) { if (props.celsius >= 100) { return <p>The water would boil.</p>; } return <p>The water would not boil.</p>; } 106
Synchronizing - Coordinator class Calculator extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: e.target.value}); render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> <BoilingVerdict celsius={parseFloat(temperature)} /> </fieldset> ); } } 107
Synchronizing - Producer class Calculator extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: e.target.value}); render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> <BoilingVerdict celsius={parseFloat(temperature)} /> </fieldset> ); } } 108
Synchronizing - Producer class TemperatureInputCelsius extends React.Component { constructor(props) { ... } render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 109
Synchronizing - Producer class TemperatureInputCelsius extends React.Component { constructor(props) { ... } handleChange render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 110
Synchronizing - Producer class TemperatureInputCelsius extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: e.target.value}); render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 111
Synchronizing - Producer class TemperatureInputCelsius extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: e.target.value}); render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 112
Synchronizing - Coordinator class Calculator extends React.Component { constructor(props) { ... } handleChange = (...?) => this.setState({temperature: ...?}); render() { const temperature = this.state.temperature; return ( <TemperatureInputCelsius ...? ...? /> <BoilingVerdict celsius={parseFloat(temperature)} /> </fieldset> ); } } 113
Synchronizing - Coordinator class Calculator extends React.Component { constructor(props) { ... } handleChange = (temperature) => this.setState({temperature: temperature}); render() { const temperature = this.state.temperature; return ( <TemperatureInputCelsius temperature={temperature} onTemperatureChange={this.handleChange} /> <BoilingVerdict celsius={parseFloat(temperature)} /> </fieldset> ); } } 114
Synchronizing - Producer class TemperatureInputCelsius extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: e.target.value}); render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 115
Synchronizing - Producer class TemperatureInputCelsius extends React.Component { constructor(props) { ... } handleChange = (e) => this.props.onTemperatureChange(e.target.value); render() { const temperature = this.props.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 116
Synchronizing - Coordinator 117 Calculator this: { handleChange } state: { temperature } props: { temperature } BoilingVeredict props: { temperature, onTemperatureChange } TemperatureInputCelsius
Synchronizing - Multiple Producer Calculator TemperatureInput => Celsius and Fahrenheit 118
Synchronizing - Producer class TemperatureInput extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: e.target.value}); render() { const temperature = this.state.temperature; const scaleName = scaleNames[this.props.scale]; return ( <fieldset> <legend>Enter temperature in {scaleName}:</ legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 119
Synchronizing - Producer class Calculator extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: ...?}); render() { const temperature = this.state.temperature; return ( <div> <TemperatureInput scale="c" ...? ...? /> <TemperatureInput scale="f" ...? ...? /> <BoilingVerdict celsius={parseFloat(temperature)} /> </div> ); } } 120
Synchronizing - Producer class TemperatureInput extends React.Component { constructor(props) { ... } handleChange = (e) => this.props.onTemperatureChange(e.target.value); render() { const temperature = this.props.temperature; const scaleName = scaleNames[this.props.scale]; return ( <fieldset> <legend>Enter temperature in {scaleName}:</ legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 121
Synchronizing - Coordinator class Calculator extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: ...?}); render() { const temperature = this.state.temperature; return ( <div> <TemperatureInput scale="c" ...? ...? /> <TemperatureInput scale="f" ...? ...? /> <BoilingVerdict celsius={parseFloat(temperature)} /> </div> ); } } 122
Coordinator - Inputs class Calculator extends React.Component { ... render() { const scale = this.state.scale; const temperature = this.state.temperature; const celsius = convertIf(scale === 'f', temperature, toCelsius); const fahrenheit = convertIf(scale === 'c', temperature, toFahrenheit); return ( <div> <TemperatureInput scale="c" temperature={celsius} ...? /> <TemperatureInput scale="f" temperature={fahrenheit} ...? /> <BoilingVerdict celsius={parseFloat(celsius)} /> </div> ); } } 123
Coordinator Outputs class Calculator extends React.Component { ... handleCelsiusChange = (temperature) => this.setState({scale: 'c', temperature}); handleFahrenheitChange = (temperature) => this.setState({scale: 'f', temperature}); render() { const scale = this.state.scale; const temperature = this.state.temperature; const celsius = convertIf(scale === 'f', temperature, toCelsius); ... <TemperatureInput scale="c" temperature={celsius} onTemperatureChange={this.handleCelsiusChange} /> <TemperatureInput scale="f" temperature={fahrenheit} onTemperatureChange={this.handleFahrenheitChange} /> ... } } 124
Coordinator Outputs function convertIf(test, temperature, convert) { if (test) { return tryConvert(temperature, convert); } return temperature; } 125
Exercise 126 https://codepen.io/drpicox/pen/ryrypJ Exercise React Temperature
Component Composition https://facebook.github.io/react/docs/composition-vs- inheritance.html
Java 1.0 128 http://web.mit.edu/java_v1.0.2/www/apibook/javag2.htm An application should override the action method (II- §1.10.1) of the button or of one of its containing windows in order to cause some action to occur. Button + action(...) MyButton + action(...)
Java 1.1 129 In 1.1, we aimed at solving some major AWT (Abstract Window Toolkit) deficiencies, with a strong focus on quality and performance. The AWT enhancements include [...], a delegation-based event model, [...]. Button + actionActionListener(...) https://www.cs.princeton.edu/courses/archive/fall97/cs461/ jdkdocs/guide/awt/index.html <i> ActionListener + actionPerformed(...) = 0 MyActionListener + actionPerformed(...) *
Java 1.2 130 When an Action object is added to such a container, the container: Creates a component that is appropriate for that container (a toolbar creates a button component, for example), Gets the appropriate property(s) from the Action object to customize the component (for example, the icon image and flyover text).... <i> Action + actionPerformed(...) = 0 + isEnabled(): bool + setEnabled(b) + getValue(key): Object + putValue(key, value) + addPropertyChangeListener(...) + removePropertyChangeListener(...) http://www.kbs.twi.tudelft.nl/Documentation/Programming/ Java/jdk1.2/api/javax/swing/Action.html
Children Composition 131 Card Contents
Children Composition <Card> <h1>Welcome</h1> <p> Find here a complete list of all the things that you love. </p> </Card> 132
Children Composition function Card(props) { return ( <div className="card"> {props.children} </div> ); } 133
Many Children Composition 134 SplitPane Left Right
Many Children Composition <div class="SplitPane"> <div class="SplitPane-left"> <Contacts /> </div> <div class="SplitPane-right"> <Chat /> </div> </div> 135
Many Children Composition function SplitPane(props) { return ( <div className="SplitPane"> <div className="SplitPane-left"> {props.left} </div> <div className="SplitPane-right"> {props.right} </div> </div> ); } 136
Many Children Composition function App() { return ( <SplitPane left={ <Contacts /> } right={ <Chat /> } /> ); } 137
Specialization function Dialog(props) { return ( <FancyBorder color="blue"> <h1 className="Dialog-title"> {props.title} </h1> <p className="Dialog-message"> {props.message} </p> </FancyBorder> ); } 138
Specialization function WelcomeDialog(props) { return ( <Dialog title="Welcome" message="Thanks for visiting!" /> ); } 139
Specialization function WelcomeDialog(props) { return ( <Dialog title="Welcome" message={ <div> <h1>Thanks!</h1> for visiting! </div> } /> ); } 140
High Order Components https://facebook.github.io/react/docs/higher-order- components.html
Like high order functions const safeConvert = (convert) => { return (temperature) => { const input = parseFloat(temperature); if (Number.isNaN(input)) { return ''; } const output = convert(input); const rounded = Math.round(output * 100) / 100; return rounded.toString(); } } 142
Like high order functions const safeConvert = (convert) => { return (temperature) => { ... } } const safeKelvinToCelsius = safeConvert(kelvinToCelsius); const celsius = safeKelvinToCelsius(kelvin); 143
Specific Component class TemperatureInputCelsius extends React.Component { handleChange = (e) => this.props.onTemperatureChange( safeCelsiusToKelvin(e.target.value); ); render() { const temperature = this.props.temperature; const celsius = safeKelvinToCelsius(temperature); return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 144
Specific Component class TemperatureInputFahrenheit extends React.Component { handleChange = (e) => this.props.onTemperatureChange( safeFahrenheitToKelvin(e.target.value); ); render() { const temperature = this.props.temperature; const celsius = safeKelvinToFahrenheit(temperature); return ( <fieldset> <legend>Enter temperature in Fahrenheit:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 145
Generic Component class TemperatureInput? extends React.Component { handleChange = (e) => this.props.onTemperatureChange( toKelvin(e.target.value); ); render() { const temperature = this.props.temperature; const local = toLocal(temperature); return ( <fieldset> <legend>Enter temperature in {scaleName}:</ legend> <input value={local} onChange={this.handleChange} /> </fieldset> ); } } 146
Generic Component function makeTemperatureInput(toKelvin, toLocal, scaleName) { return class extends React.Component { handleChange = (e) => this.props.onTemperatureChange( toKelvin(e.target.value); ); render() { const temperature = this.props.temperature; const local = toLocal(temperature); return ( <fieldset> <legend>Enter temperature in {scaleName}:</legend> <input value={local} onChange={this.handleChange} /> </fieldset> ); } } } 147
Using Generic Component const TemperatureInputCelsius = makeTemperatureInput( safeCelsiusToKelvin, safeKelvinToCelsius, 'Celsius' ); const TemperatureInputFahrenheit = makeTemperatureInput( safeFahrenheitToKelvin, safeKelvinToFahrenheit, 'Fahrenheit' ); const TemperatureInputKelvin = makeTemperatureInput( identity, identity, 'Kelvin' ); 148
Using Generic Component const TemperatureInputCelsius = makeTemperatureInput( safeConvert(celsiusToKelvin), safeConvert(kelvinToCelsius), 'Celsius' ); const TemperatureInputFahrenheit = makeTemperatureInput( safeConvert(fahrenheitToKelvin), safeConvert(kelvinToFahrenheit), 'Fahrenheit' ); const TemperatureInputKelvin = makeTemperatureInput( (x) => x, (x) => x, 'Kelvin' ); 149
High Order Component class Thermostat extends React.Component { constructor(props) { ... } handleChangeMax = (e) => ... handleChangeMin = (e) => ... render() { return ( <div> Max <TemperatureInput temperature={this.state.max} onTemperatureChange={this.handleChangeMax} /> Min <TemperatureInput temperature={this.state.min} onTemperatureChange={this.handleChangeMin} /> </div> ); } } 150
High Order Component function makeThermostat(TemperatureInput) { return class extends React.Component { constructor(props) { ... } handleChangeMax = (e) => ... handleChangeMin = (e) => ... render() { return ( <div> Max <TemperatureInput temperature={this.state.max} onTemperatureChange={this.handleChangeMax} /> Min <TemperatureInput temperature={this.state.max} onTemperatureChange={this.handleChangeMax} /> </div> ); } } } 151
High Order Component function makeThermostat(TemperatureInput) { return class extends React.Component { ... } } const ThermostatCelsius = makeThermostat(TemperatureInputCelsius); const ThermostatFahrenheit = makeThermostat(TemperatureInputFahrenheit); const ThermostatKelvin = makeThermostat(TemperatureInputKelvin); 152
Debug: displayName function makeTemperatureInput( toKelvin, toFahrenheit, scaleName ) { class TemperatureInput extends React.Component { ... } TemperatureInput.displayName = `TemperatureInput(${scaleName})`; return TemperatureInput; } 153
Debug: displayName function makeThermostat(TemperatureInput) { class Thermostat extends React.Component { ... } Thermostat.displayName = `Thermostat(${getDisplayName(TemperatureInput)})`; return Thermostat; } 154
Warning! // DON'T use HOCs in render functions! const App = () => { const TemperatureInputAbc = makeTemperatureInput(a, b, c); return <TemperatureInputAbc />; } class App extends React.Component { render() { const TemperatureInputAbc = makeTemperatureInput(a, b, c); return <TemperatureInputAbc />; } } 155
More in docs • Use HOCs For Cross-Cutting Concerns • Don't Mutate the Original Component. 
 Use Composition. • Convention: Pass Unrelated Props Through to the Wrapped Component • Convention: Maximizing Composability • Convention: Wrap the Display Name for Easy Debugging • Caveats 156
Containers https://medium.com/@dan_abramov/smart-and-dumb- components-7ca2f9a7c7d0
Convention • Types of components • Routers • Containers • Presentational 158
Routes • Routers • Decides which component to render • Create components dynamically • Usually provided by library 159
Containers • Knows how to load or mutate data • Observe Stores • Dispatch Actions • Other system interactions • Always stateful (class notation) • Renders nothing • Delegates render to a Presentational Component • Configures its props 160
Presentational • Knows how to render things • Data and callbacks only via props • does not interact with the application • Usually functional (not need state) • Also called Components 161
class Clock extends React.Component { constructor(props) { ... } componentDidMount() { this.timerId = setInterval( () => this.setState({time: Date.now()}), 1000 ); } componentWillUnmount() { clearInterval(this.timerId); } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.time}.</h2> </div> ); } } 162 Presentational Container
class Clock extends React.Component { constructor(props) { ... } componentDidMount() { this.timerId = setInterval( () => this.setState({time: Date.now()}), 1000 ); } componentWillUnmount() { clearInterval(this.timerId); } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.time}.</h2> </div> ); } } 163 Presentational Container
Presentational Refactor function Clock(props) { return ( <div> <h1>Hello, world!</h1> <h2>It is {props.time}.</h2> </div> ); } 164
Container Refactor class IntervalClock extends React.Component { constructor(props) { ... } componentDidMount() { this.timerId = setInterval( () => this.setState({time: Date.now()}), 1000 ); } componentWillUnmount() { clearInterval(this.timerId); } render() { return <Clock time={this.state.time} />; } } 165
Container Refactor function makeIntervalClock(Clock) { return class extends React.Component { constructor(props) { ... } componentDidMount() { this.timerId = setInterval( () => this.setState({time: Date.now()}), 1000 ); } componentWillUnmount() { clearInterval(this.timerId); } render() { return <Clock time={this.state.time} />; } }; } 166
PropTypes https://facebook.github.io/react/docs/typechecking-with- proptypes.html
PropTypes import PropTypes from 'prop-types'; export default function Welcome(props) { return ( <h1>Hello, {props.name}</h1> ); } Welcome.propTypes = { name: PropTypes.string }; 168 https://facebook.github.io/react/docs/typechecking-with-proptypes.html
PropTypes import PropTypes from 'prop-types'; export default class Welcome extends React.Component { render() { return ( <h1>Hello, {this.props.name}</h1> ); } } Welcome.propTypes = { name: PropTypes.string.isRequired }; 169 https://facebook.github.io/react/docs/typechecking-with-proptypes.html

ReactJS for Programmers

  • 1.
  • 2.
  • 3.
  • 4.
    FB about MVC 4 “MVCworks pretty well for small applications… but it doesn’t make room for new features.” “Flux is a single direction data flow, that avoids all the arrows going on all directions what makes really hard to understand the system.”
  • 5.
    FB about Chat 5 “Let’ssee a real good example: FB chat” “How we get to the point, so we were annoying our users so much the just they wanted us to fix chat?” “The problems here were: • The code has no structure • It was very imperative, that makes it fragile • It loose a lot of the original intend behind it, its hard to tell what it tries [to do] • Add more features only gets this code larger • We had our most annoying chat bug happen over and over • We were always fixing some pretty good edge case, the whole system was fragile • … • This code becomes more fragile with the time. • No member of the team wanted to touch it, they wanted to jump to any other bug.”
  • 6.
    FB about Rendering 6 “ImperativeRendering” “If renders all each time the screen flickers” “We wanted always render all, no matter what” “Here is where React comes in.”
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
    Hello World <!-- hello-world.html--> <div id="root"></div> // hello-world.js const virtualDom = React.createElement( 'h1', null, 'Hello, world!' ); ReactDOM.render( virtualDom, document.getElementById('root') ); 11
  • 12.
    Hello World <!-- hello-world.html--> <div id="root"></div> // hello-world.jsx const virtualDom = <h1>Hello, world!</h1>; ReactDOM.render( virtualDom, document.getElementById('root') ); 12
  • 13.
    Hello World <!-- hello-world.html--> <div id="root"></div> // hello-world.jsx ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('root') ); 13 https://facebook.github.io/react/docs/hello-world.html
  • 14.
    Hello World // Thisis translated by Babel to... ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('root') ); // ...to this ReactDOM.render(React.createElement( 'h1', null, 'Hello, world!' ), document.getElementById('root')); 14 https://babeljs.io/repl
  • 15.
    • ReactJS onlyupdates what changes • Although everything is regenerated every time • Open countdown example at codepen with chrome
 http://codepen.io/drpicox/pen/yMKWGN • Inspect page • You can see that only the span is updated
 (although this crude code changes everything) Do not fear change 15
  • 16.
  • 17.
    JSX // Just sugarsyntax const element = <h1>Hello, world!</h1>; // It is just one object const element = React.createElement( "h1", null, "Hello, world!" ); // one object that you can manipulate as object 17
  • 18.
    JSX // May I? console.log(<h1>Hello!</h1>); console.log(<h1>Hello!</h1>.toString()); console.log([<h1>Hello!</h1>]); console.log({hello:<h1>Hello!</h1>}); console.log((() => <h1>Hello!</h1>)()); const salute = (what) => <h1>{what}!</h1>; console.log(salute('Hello')); 18
  • 19.
    JSX // Have multipleelements (but one wrapper) const element = <h1><span>Hello</span>, world!</h1>; // Generates const element = React.createElement( "h1", null, React.createElement( "span", null, "Hello" ), ", world!" ); 19
  • 20.
    JSX // It canbe multiline (be careful using return) const element = <h1> Hello, world! </h1>; 20
  • 21.
    JSX // It canbe multiline (better) const element = <h1> Hello, world! </h1>; 21
  • 22.
    JSX // It canbe multiline (recommended) const element = ( <h1> Hello, world! </h1> ); 22
  • 23.
    JSX - Interpolation //Interpolate any JS expression const name = 'bob hoskings'; const element = ( <h1> Hello, {name}! </h1> ); // It is just one argument more const element = React.createElement( 'h1', null, 'Hello, ', name, '!' ); 23
  • 24.
    JSX - Interpolation //Interpolate any JS expression const user = {name:'bob', lastName:'hoskings'}; const element = ( <h1> Hello, {user.name + ' ' + user.lastName}! </h1> ); // It is still just one argument more const element = React.createElement( 'h1', null, 'Hello, ', user.name + ' ' + user.lastName, '!' ); 24
  • 25.
    JSX - Interpolation //any expression, including JSX const element = <h1>Hello, {<span>world</span>}!</h1> // It is still just one argument more const element = React.createElement( "h1", null, "Hello, ", React.createElement( "span", null, "world" ), "!" ); 25
  • 26.
    JSX - Properties //Add attributes const element = <div tabIndex="0">...</div>; // Is transformed into a property object (alias props) const element = React.createElement( "div", { tabIndex: "0" }, "..." ); 26
  • 27.
    JSX - Properties //Add computed attributes const element = <img src={user.imageUrl} />; // Is transformed into a property object (alias props) const element = React.createElement( "img", { src: user.imageUrl }, ); 27
  • 28.
    JSX - Properties //Set variable attributes const options = { autoplay: true, muted: true, }; const element = <video src="wow.mp4" {...options} />; // Are merged into attribute const element = React.createElement( "video", { src: "wow.mp4", ...options }, ); 28
  • 29.
    JSX - Properties //Set variable attributes const options = { autoplay: true, muted: true, }; const element = <video src="wow.mp4" {...options} />; // Are merged into attribute const element = React.createElement( "video", { src: "wow.mp4", autoplay: true, muted: true }, ); 29
  • 30.
    Properties vs Attributes <!--html uses attributes --> <img id="hello" src="hello.jpg"> <!-- javascript uses properties --> const helloImg = document.getElementById('hello'); console.log(helloImg.src); 30
  • 31.
    Properties vs Attributes <!--html uses attributes --> <h1 id="hello" class="big">Hello</h1> <!-- javascript uses properties --> const helloH1 = document.getElementById('hello'); console.log(helloH1.className); 31
  • 32.
    JSX - Properties //Because bad IE8 habits: className instead of class const element = <h1 className="big">Hello</h1>; // Is rendered as real html attribute: <h1 class="big">Hello</h1> 32
  • 33.
    JSX - Cross-site-scripting //It is just a string const title = response.veryMaliciousInputString; // This is save const element = <h1>{title}</h1>; 33
  • 34.
    JSX - XML //Is XML-like const element = ( <div> Hello <br/> World </div> ); // Elements must be terminated const element = ( <div> Hello <br> World </div> ); 34
  • 35.
  • 36.
    Rendering Elements // JSXelements are just memory objects const element = <h1>Hello, world</h1>; 36
  • 37.
    Rendering Elements // Givenan html DOM element <div id="root"></div> // they can be rendered inside DOM const domElement = document.getElementById('root'); const reactElement = <h1>Hello, world</h1>; ReactDOM.render( reactElement, /* into */ domElement ); 37
  • 38.
    Rendering Elements // Asmany times as you want // react only updates changes const domElement = document.getElementById('root'); let n = 0; setInterval(() => { ReactDOM.render(<h1>{n += 1}</h1>, domElement); }, 1000); 38
  • 39.
  • 40.
    Components // Can bedefined as functions const HelloWorld = () => { return <h1>Hello, world</h1>; }; export default HelloWorld; 40
  • 41.
    Components // Can bedefined as functions const HelloWorld = () => { return <h1>Hello, world</h1>; }; export default HelloWorld; // Can be defined as classes export default class HelloWorld extends React.Component { render() { return <h1>Hello, world</h1>; } } 41
  • 42.
    Component and properties constelement = <Welcome name="Dave" />; 42
  • 43.
    Component and properties constelement = <Welcome name="Dave" />; // Remember: it is like write const element = React.createElement( Welcome, { name: "Dave" }, // are called props ); 43
  • 44.
    Component and properties constelement = <Welcome name="Dave" />; // This is how you define the Component to use props function Welcome(props) { return ( <h1>Hello {props.name}!</h1> ); } 44
  • 45.
    Component and properties constelement = <Welcome name="Dave" />; // This is how you define the Component to use props class Welcome extends React.Component { constructor(props) { super(props); } render() { return ( <h1>Hello {this.rops.name}!</h1> ); } } 45
  • 46.
    Composing Components function App(){ return ( <div> <Welcome name="Alice" /> </div> ); } 46
  • 47.
    Composing Components function App(){ return ( <div> <Welcome name="Alice" /> <Welcome name="Bob" /> <Welcome name="Dave" /> </div> ); } 47
  • 48.
    Refactoring Components function Component(props){ return ( <div className="Comment"> <div className="UserInfo"> <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); } 48
  • 49.
    Refactoring Components function Avatar(props){ return ( <img className="Avatar" src={props.user.avatarUrl} alt={props.user.name} /> ); } 49 <img className="Avatar" src={props.author.avatarUrl} alt={props.author.name} />
  • 50.
    Refactoring Components function Component(props){ return ( <div className="Comment"> <div className="UserInfo"> <Avatar user={props.author} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); } 50
  • 51.
    Refactoring Components function Component(props){ return ( <div className="Comment"> <div className="UserInfo"> <Avatar user={props.author} /> <div className="UserInfo-name"> {props.author.name} </div> </div> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); } 51
  • 52.
    Refactoring Components function UserInfo(props){ return ( <div className="UserInfo"> <Avatar user={props.user} /> <div className="UserInfo-name"> {props.user.name} </div> </div> ); } 52
  • 53.
    Refactoring Components function Component(props){ return ( <div className="Comment"> <UserInfo user={props.author} /> <div className="Comment-text"> {props.text} </div> <div className="Comment-date"> {formatDate(props.date)} </div> </div> ); } 53
  • 54.
    Props are Read-Only //Render must be read-only function MissbehavingComponent(props) { return <h1>{props.n += 1}</h1>; } 54
  • 55.
  • 56.
    Function to ClassComponent function Clock(props) { return ( <div> <h1>Hello, world!</h1> <h2>It is {props.time}.</h2> </div> ); } 56
  • 57.
    Function to ClassComponent class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.time}.</h2> </div> ); } } 57
  • 58.
    Function to ClassComponent class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.time}.</h2> </div> ); } } 58
  • 59.
    Setup State class Clockextends React.Component { constructor(props) { super(props); this.state = {time: Date.now()}; } render() { ... } } 59
  • 60.
    Setup State class Clockextends React.Component { constructor(props) { ... } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.time}.</h2> </div> ); } } 60
  • 61.
    Lifecycle Methods class Clockextends React.Component { constructor(props) { ... } componentDidMount() { /* TODO */ } componentWillUnmount() { /* TODO */ } render() { ... } } 61
  • 62.
    Lifecycle Methods class Clockextends React.Component { constructor(props) { ... } componentDidMount() { this.timerId = setInterval( () => this.setState({time: Date.now()}), 1000 ); } componentWillUnmount() { /* TODO */ } render() { ... } } 62
  • 63.
    Lifecycle Methods class Clockextends React.Component { constructor(props) { ... } componentDidMount() { ... } componentWillUnmount() { clearInterval(this.timerId); } render() { ... } } 63
  • 64.
    setState Asynchronous // Wrong! setState({counter:this.state.counter + 1}); // Correct setState((prevState, props) => ({ counter: prevState.counter + 1 }); 64
  • 65.
    States are Merged constructor(){ this.state = {posts: [], users: []}; } componentDidMount() { getPosts((posts) => this.setState({posts})); getUsers((users) => this.setState({users})); } 65
  • 66.
    State Isolated function App(){ return ( <div> <Clock /> <Clock /> <Clock /> </div> ); } 66
  • 67.
  • 68.
    onEvent={fn} function ButtonLogHello() { functionhandleClick() { console.log('The button is clicked'); } return ( <button onClick={handleClick}> click me </button> ); } 68
  • 69.
    onEvent={fn} // It receivedthe event as parameter function ALogHello() { function handleClick(ev) { ev.preventDefault(); console.log('The link is clicked'); } return ( <a href="#" onClick={handleClick}> click me </a> ); } 69
  • 70.
    onEvent={fn} // It caninline functions (are expressions) function DivLogHello() { return ( <button onClick={() => console.log('oh')}> click me </button> ); } 70
  • 71.
    Toggle Example class Toggleextends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; } ... 71
  • 72.
    Toggle Example class Toggleextends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; } handleClick() { ... } ... 72
  • 73.
    Toggle Example class Toggleextends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; // This binding is necessary by `this` this.handleClick = this.handleClick.bind(this); } handleClick() { ... } ... 73
  • 74.
    Toggle Example class Toggleextends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; // Fat arrow function preserves `this` this.handleClick = () => { ... }; } ... 74
  • 75.
    Toggle Example class Toggleextends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; } // Stage 2 alternative (recommended official) handleClick = () => { ... } ... 75
  • 76.
    Toggle Example class Toggleextends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; } handleClick = () => { … } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'on' : 'off'} </button> ); } } 76
  • 77.
    Toggle Example class Toggleextends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; } handleClick = () => { // Warning, wrong implementation this.setState({ isToggleOn: !this.state.isToggleOn }); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'on' : 'off'} </button> ); } } 77
  • 78.
    Toggle Example class Toggleextends React.Component { constructor(props) { super(props); this.state = { isToggleOn: true }; } handleClick = () => { this.setState((prevState) => ({ isToggleOn: !prevState.isToggleOn, }); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'on' : 'off'} </button> ); } } 78
  • 79.
  • 80.
    Conditional Rendering function UserGreeting(props){ return <h1>Welcome back!</h1>; } function GuestGreeting(props) { return <h1>Please sign up.</h1>; } 80
  • 81.
    By return function Greeting(props){ const isLoggedIn = props.isLoggedIn; if (isLoggedIn) { return <UserGreeting />; } return <GuestGreeting />; } ReactDOM.render( // Try changing to isLoggedIn={true}: <Greeting isLoggedIn={false} />, document.getElementById('root') ); 81
  • 82.
    By variable render() { constisLoggedIn = this.state.isLoggedIn; let button = null; if (isLoggedIn) { button = <LogoutButton onClick={this.handleLogoutClick} / >; } else { button = <LoginButton onClick={this.handleLoginClick} />; } return ( <div> <Greeting isLoggedIn={isLoggedIn} /> {button} </div> ); } 82
  • 83.
    By && expression functionMailbox(props) { const unreadMessages = props.unreadMessages; return ( <div> <h1>Hello!</h1> {unreadMessages.length > 0 && <h2> You have {unreadMessages.length} unread messages. </h2> } </div> ); } 83
  • 84.
    By ?: expression render(){ const isLoggedIn = this.state.isLoggedIn; return ( <div> {isLoggedIn ? ( <LogoutButton onClick={this.handleLogoutClick} /> ) : ( <LoginButton onClick={this.handleLoginClick} /> )} </div> ); } 84
  • 85.
    Prevent Render function WarningBanner(props){ if (!props.warn) { return null; } return ( <div className="warning"> Warning! </div> ); } 85
  • 86.
  • 87.
    Remember... const numbers =[1, 2, 3, 4, 5]; const doubled = numbers.map((n) => n * 2); console.log(doubled); // [2, 4, 6, 8, 10] 87
  • 88.
    Render Multiple // Rendera reactElements array const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((number) => <li>{number}</li> ); ReactDOM.render( <ul>{listItems}</ul>, document.getElementById('root') ); 88
  • 89.
    Render Multiple // Refactorinto component function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => <li>{number}</li> ); return ( <ul>{listItems}</ul> ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={numbers} />, document.getElementById('root') ); 89
  • 90.
    Key function NumberList(props) { constnumbers = props.numbers; const listItems = numbers.map((number) => <li key={number.toString()}> {number} </li> ); return ( <ul>{listItems}</ul> ); } 90
  • 91.
    Key - index consttodoItems = todos.map((todo, index) => // Only do this if items have no stable IDs <li key={index}> {todo.text} </li> ); 91
  • 92.
    Key - RefactorComponent function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => <li key={number.toString()}> {number} </li> ); return ( <ul>{listItems}</ul> ); } 92
  • 93.
    Key - RefactorComponent function ListItem(props) { return ( // Wrong! should not be here <li key="props.number.toString()"> {props.number} </li> ); } 93
  • 94.
    Key - RefactorComponent function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => // Correct: key must remain with map <ListItem key={number.toString()} number={number} /> {number} ); return ( <ul>{listItems}</ul> ); } 94
  • 95.
    Key - Uniqueness //Cannot repeat keys in the same list, but... function DoubleList(props) { return ( <div> <NumberList numbers={props.numbers} /> <NumberList numbers={props.numbers} /> <NumberList numbers={props.numbers} /> </div> ); } 95
  • 96.
    .map inside JSX functionNumberList(props) { const numbers = props.numbers; return ( <ul> {numbers.map((number) => <ListItem key={number.toString()} value={number} /> )} </ul> ); } 96
  • 97.
  • 98.
    Controlled Components class NameFormextends React.Component { constructor() { ... } handleChange = (event) => { this.setState({value: event.target.value}); } handleSubmit = (event) => { ... } render() { return ( <form onSubmit={this.handleSubmit}> <input type="text" value={this.state.value} onChange={this.handleChange} /> </form> ); } } 98
  • 99.
    Controlled Components class NameFormextends React.Component { constructor(props) { super(props); this.state = {value: ''}; } handleChange = (event) => { ... } handleSubmit = (event) => { ... } render() { ... } } 99
  • 100.
    Controlled Components class NameFormextends React.Component { constructor() { ... } handleChange = (event) => { this.setState({ value: event.target.value.toLowerCase() }); } handleSubmit = (event) => { ... } render() { ... } } 100
  • 101.
  • 102.
  • 103.
    Multiple Inputs <input name="isGoing" type="checkbox" checked={this.state.isGoing} onChange={this.handleInputChange}/> <input name="numberOfGuests" type="number" value={this.state.numberOfGuests} onChange={this.handleInputChange} /> 103
  • 104.
    Multiple Inputs handleInputChange(event) { consttarget = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name; this.setState({ [name]: value }); } 104
  • 105.
  • 106.
    Synchronizing - Consumer //We want an <input> to generate new values for function BoilingVerdict(props) { if (props.celsius >= 100) { return <p>The water would boil.</p>; } return <p>The water would not boil.</p>; } 106
  • 107.
    Synchronizing - Coordinator classCalculator extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: e.target.value}); render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> <BoilingVerdict celsius={parseFloat(temperature)} /> </fieldset> ); } } 107
  • 108.
    Synchronizing - Producer classCalculator extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: e.target.value}); render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> <BoilingVerdict celsius={parseFloat(temperature)} /> </fieldset> ); } } 108
  • 109.
    Synchronizing - Producer classTemperatureInputCelsius extends React.Component { constructor(props) { ... } render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 109
  • 110.
    Synchronizing - Producer classTemperatureInputCelsius extends React.Component { constructor(props) { ... } handleChange render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 110
  • 111.
    Synchronizing - Producer classTemperatureInputCelsius extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: e.target.value}); render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 111
  • 112.
    Synchronizing - Producer classTemperatureInputCelsius extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: e.target.value}); render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 112
  • 113.
    Synchronizing - Coordinator classCalculator extends React.Component { constructor(props) { ... } handleChange = (...?) => this.setState({temperature: ...?}); render() { const temperature = this.state.temperature; return ( <TemperatureInputCelsius ...? ...? /> <BoilingVerdict celsius={parseFloat(temperature)} /> </fieldset> ); } } 113
  • 114.
    Synchronizing - Coordinator classCalculator extends React.Component { constructor(props) { ... } handleChange = (temperature) => this.setState({temperature: temperature}); render() { const temperature = this.state.temperature; return ( <TemperatureInputCelsius temperature={temperature} onTemperatureChange={this.handleChange} /> <BoilingVerdict celsius={parseFloat(temperature)} /> </fieldset> ); } } 114
  • 115.
    Synchronizing - Producer classTemperatureInputCelsius extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: e.target.value}); render() { const temperature = this.state.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 115
  • 116.
    Synchronizing - Producer classTemperatureInputCelsius extends React.Component { constructor(props) { ... } handleChange = (e) => this.props.onTemperatureChange(e.target.value); render() { const temperature = this.props.temperature; return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 116
  • 117.
    Synchronizing - Coordinator 117 Calculator this:{ handleChange } state: { temperature } props: { temperature } BoilingVeredict props: { temperature, onTemperatureChange } TemperatureInputCelsius
  • 118.
    Synchronizing - MultipleProducer Calculator TemperatureInput => Celsius and Fahrenheit 118
  • 119.
    Synchronizing - Producer classTemperatureInput extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: e.target.value}); render() { const temperature = this.state.temperature; const scaleName = scaleNames[this.props.scale]; return ( <fieldset> <legend>Enter temperature in {scaleName}:</ legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 119
  • 120.
    Synchronizing - Producer classCalculator extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: ...?}); render() { const temperature = this.state.temperature; return ( <div> <TemperatureInput scale="c" ...? ...? /> <TemperatureInput scale="f" ...? ...? /> <BoilingVerdict celsius={parseFloat(temperature)} /> </div> ); } } 120
  • 121.
    Synchronizing - Producer classTemperatureInput extends React.Component { constructor(props) { ... } handleChange = (e) => this.props.onTemperatureChange(e.target.value); render() { const temperature = this.props.temperature; const scaleName = scaleNames[this.props.scale]; return ( <fieldset> <legend>Enter temperature in {scaleName}:</ legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 121
  • 122.
    Synchronizing - Coordinator classCalculator extends React.Component { constructor(props) { ... } handleChange = (e) => this.setState({temperature: ...?}); render() { const temperature = this.state.temperature; return ( <div> <TemperatureInput scale="c" ...? ...? /> <TemperatureInput scale="f" ...? ...? /> <BoilingVerdict celsius={parseFloat(temperature)} /> </div> ); } } 122
  • 123.
    Coordinator - Inputs classCalculator extends React.Component { ... render() { const scale = this.state.scale; const temperature = this.state.temperature; const celsius = convertIf(scale === 'f', temperature, toCelsius); const fahrenheit = convertIf(scale === 'c', temperature, toFahrenheit); return ( <div> <TemperatureInput scale="c" temperature={celsius} ...? /> <TemperatureInput scale="f" temperature={fahrenheit} ...? /> <BoilingVerdict celsius={parseFloat(celsius)} /> </div> ); } } 123
  • 124.
    Coordinator Outputs class Calculatorextends React.Component { ... handleCelsiusChange = (temperature) => this.setState({scale: 'c', temperature}); handleFahrenheitChange = (temperature) => this.setState({scale: 'f', temperature}); render() { const scale = this.state.scale; const temperature = this.state.temperature; const celsius = convertIf(scale === 'f', temperature, toCelsius); ... <TemperatureInput scale="c" temperature={celsius} onTemperatureChange={this.handleCelsiusChange} /> <TemperatureInput scale="f" temperature={fahrenheit} onTemperatureChange={this.handleFahrenheitChange} /> ... } } 124
  • 125.
    Coordinator Outputs function convertIf(test,temperature, convert) { if (test) { return tryConvert(temperature, convert); } return temperature; } 125
  • 126.
  • 127.
  • 128.
    Java 1.0 128 http://web.mit.edu/java_v1.0.2/www/apibook/javag2.htm An applicationshould override the action method (II- §1.10.1) of the button or of one of its containing windows in order to cause some action to occur. Button + action(...) MyButton + action(...)
  • 129.
    Java 1.1 129 In 1.1,we aimed at solving some major AWT (Abstract Window Toolkit) deficiencies, with a strong focus on quality and performance. The AWT enhancements include [...], a delegation-based event model, [...]. Button + actionActionListener(...) https://www.cs.princeton.edu/courses/archive/fall97/cs461/ jdkdocs/guide/awt/index.html <i> ActionListener + actionPerformed(...) = 0 MyActionListener + actionPerformed(...) *
  • 130.
    Java 1.2 130 When anAction object is added to such a container, the container: Creates a component that is appropriate for that container (a toolbar creates a button component, for example), Gets the appropriate property(s) from the Action object to customize the component (for example, the icon image and flyover text).... <i> Action + actionPerformed(...) = 0 + isEnabled(): bool + setEnabled(b) + getValue(key): Object + putValue(key, value) + addPropertyChangeListener(...) + removePropertyChangeListener(...) http://www.kbs.twi.tudelft.nl/Documentation/Programming/ Java/jdk1.2/api/javax/swing/Action.html
  • 131.
  • 132.
    Children Composition <Card> <h1>Welcome</h1> <p> Find herea complete list of all the things that you love. </p> </Card> 132
  • 133.
    Children Composition function Card(props){ return ( <div className="card"> {props.children} </div> ); } 133
  • 134.
  • 135.
    Many Children Composition <divclass="SplitPane"> <div class="SplitPane-left"> <Contacts /> </div> <div class="SplitPane-right"> <Chat /> </div> </div> 135
  • 136.
    Many Children Composition functionSplitPane(props) { return ( <div className="SplitPane"> <div className="SplitPane-left"> {props.left} </div> <div className="SplitPane-right"> {props.right} </div> </div> ); } 136
  • 137.
    Many Children Composition functionApp() { return ( <SplitPane left={ <Contacts /> } right={ <Chat /> } /> ); } 137
  • 138.
    Specialization function Dialog(props) { return( <FancyBorder color="blue"> <h1 className="Dialog-title"> {props.title} </h1> <p className="Dialog-message"> {props.message} </p> </FancyBorder> ); } 138
  • 139.
    Specialization function WelcomeDialog(props) { return( <Dialog title="Welcome" message="Thanks for visiting!" /> ); } 139
  • 140.
    Specialization function WelcomeDialog(props) { return( <Dialog title="Welcome" message={ <div> <h1>Thanks!</h1> for visiting! </div> } /> ); } 140
  • 141.
  • 142.
    Like high orderfunctions const safeConvert = (convert) => { return (temperature) => { const input = parseFloat(temperature); if (Number.isNaN(input)) { return ''; } const output = convert(input); const rounded = Math.round(output * 100) / 100; return rounded.toString(); } } 142
  • 143.
    Like high orderfunctions const safeConvert = (convert) => { return (temperature) => { ... } } const safeKelvinToCelsius = safeConvert(kelvinToCelsius); const celsius = safeKelvinToCelsius(kelvin); 143
  • 144.
    Specific Component class TemperatureInputCelsiusextends React.Component { handleChange = (e) => this.props.onTemperatureChange( safeCelsiusToKelvin(e.target.value); ); render() { const temperature = this.props.temperature; const celsius = safeKelvinToCelsius(temperature); return ( <fieldset> <legend>Enter temperature in Celsius:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 144
  • 145.
    Specific Component class TemperatureInputFahrenheitextends React.Component { handleChange = (e) => this.props.onTemperatureChange( safeFahrenheitToKelvin(e.target.value); ); render() { const temperature = this.props.temperature; const celsius = safeKelvinToFahrenheit(temperature); return ( <fieldset> <legend>Enter temperature in Fahrenheit:</legend> <input value={temperature} onChange={this.handleChange} /> </fieldset> ); } } 145
  • 146.
    Generic Component class TemperatureInput?extends React.Component { handleChange = (e) => this.props.onTemperatureChange( toKelvin(e.target.value); ); render() { const temperature = this.props.temperature; const local = toLocal(temperature); return ( <fieldset> <legend>Enter temperature in {scaleName}:</ legend> <input value={local} onChange={this.handleChange} /> </fieldset> ); } } 146
  • 147.
    Generic Component function makeTemperatureInput(toKelvin,toLocal, scaleName) { return class extends React.Component { handleChange = (e) => this.props.onTemperatureChange( toKelvin(e.target.value); ); render() { const temperature = this.props.temperature; const local = toLocal(temperature); return ( <fieldset> <legend>Enter temperature in {scaleName}:</legend> <input value={local} onChange={this.handleChange} /> </fieldset> ); } } } 147
  • 148.
    Using Generic Component constTemperatureInputCelsius = makeTemperatureInput( safeCelsiusToKelvin, safeKelvinToCelsius, 'Celsius' ); const TemperatureInputFahrenheit = makeTemperatureInput( safeFahrenheitToKelvin, safeKelvinToFahrenheit, 'Fahrenheit' ); const TemperatureInputKelvin = makeTemperatureInput( identity, identity, 'Kelvin' ); 148
  • 149.
    Using Generic Component constTemperatureInputCelsius = makeTemperatureInput( safeConvert(celsiusToKelvin), safeConvert(kelvinToCelsius), 'Celsius' ); const TemperatureInputFahrenheit = makeTemperatureInput( safeConvert(fahrenheitToKelvin), safeConvert(kelvinToFahrenheit), 'Fahrenheit' ); const TemperatureInputKelvin = makeTemperatureInput( (x) => x, (x) => x, 'Kelvin' ); 149
  • 150.
    High Order Component classThermostat extends React.Component { constructor(props) { ... } handleChangeMax = (e) => ... handleChangeMin = (e) => ... render() { return ( <div> Max <TemperatureInput temperature={this.state.max} onTemperatureChange={this.handleChangeMax} /> Min <TemperatureInput temperature={this.state.min} onTemperatureChange={this.handleChangeMin} /> </div> ); } } 150
  • 151.
    High Order Component functionmakeThermostat(TemperatureInput) { return class extends React.Component { constructor(props) { ... } handleChangeMax = (e) => ... handleChangeMin = (e) => ... render() { return ( <div> Max <TemperatureInput temperature={this.state.max} onTemperatureChange={this.handleChangeMax} /> Min <TemperatureInput temperature={this.state.max} onTemperatureChange={this.handleChangeMax} /> </div> ); } } } 151
  • 152.
    High Order Component functionmakeThermostat(TemperatureInput) { return class extends React.Component { ... } } const ThermostatCelsius = makeThermostat(TemperatureInputCelsius); const ThermostatFahrenheit = makeThermostat(TemperatureInputFahrenheit); const ThermostatKelvin = makeThermostat(TemperatureInputKelvin); 152
  • 153.
    Debug: displayName function makeTemperatureInput( toKelvin,toFahrenheit, scaleName ) { class TemperatureInput extends React.Component { ... } TemperatureInput.displayName = `TemperatureInput(${scaleName})`; return TemperatureInput; } 153
  • 154.
    Debug: displayName function makeThermostat(TemperatureInput){ class Thermostat extends React.Component { ... } Thermostat.displayName = `Thermostat(${getDisplayName(TemperatureInput)})`; return Thermostat; } 154
  • 155.
    Warning! // DON'T useHOCs in render functions! const App = () => { const TemperatureInputAbc = makeTemperatureInput(a, b, c); return <TemperatureInputAbc />; } class App extends React.Component { render() { const TemperatureInputAbc = makeTemperatureInput(a, b, c); return <TemperatureInputAbc />; } } 155
  • 156.
    More in docs •Use HOCs For Cross-Cutting Concerns • Don't Mutate the Original Component. 
 Use Composition. • Convention: Pass Unrelated Props Through to the Wrapped Component • Convention: Maximizing Composability • Convention: Wrap the Display Name for Easy Debugging • Caveats 156
  • 157.
  • 158.
    Convention • Types ofcomponents • Routers • Containers • Presentational 158
  • 159.
    Routes • Routers • Decideswhich component to render • Create components dynamically • Usually provided by library 159
  • 160.
    Containers • Knows howto load or mutate data • Observe Stores • Dispatch Actions • Other system interactions • Always stateful (class notation) • Renders nothing • Delegates render to a Presentational Component • Configures its props 160
  • 161.
    Presentational • Knows howto render things • Data and callbacks only via props • does not interact with the application • Usually functional (not need state) • Also called Components 161
  • 162.
    class Clock extendsReact.Component { constructor(props) { ... } componentDidMount() { this.timerId = setInterval( () => this.setState({time: Date.now()}), 1000 ); } componentWillUnmount() { clearInterval(this.timerId); } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.time}.</h2> </div> ); } } 162 Presentational Container
  • 163.
    class Clock extendsReact.Component { constructor(props) { ... } componentDidMount() { this.timerId = setInterval( () => this.setState({time: Date.now()}), 1000 ); } componentWillUnmount() { clearInterval(this.timerId); } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.time}.</h2> </div> ); } } 163 Presentational Container
  • 164.
    Presentational Refactor function Clock(props){ return ( <div> <h1>Hello, world!</h1> <h2>It is {props.time}.</h2> </div> ); } 164
  • 165.
    Container Refactor class IntervalClockextends React.Component { constructor(props) { ... } componentDidMount() { this.timerId = setInterval( () => this.setState({time: Date.now()}), 1000 ); } componentWillUnmount() { clearInterval(this.timerId); } render() { return <Clock time={this.state.time} />; } } 165
  • 166.
    Container Refactor function makeIntervalClock(Clock){ return class extends React.Component { constructor(props) { ... } componentDidMount() { this.timerId = setInterval( () => this.setState({time: Date.now()}), 1000 ); } componentWillUnmount() { clearInterval(this.timerId); } render() { return <Clock time={this.state.time} />; } }; } 166
  • 167.
  • 168.
    PropTypes import PropTypes from'prop-types'; export default function Welcome(props) { return ( <h1>Hello, {props.name}</h1> ); } Welcome.propTypes = { name: PropTypes.string }; 168 https://facebook.github.io/react/docs/typechecking-with-proptypes.html
  • 169.
    PropTypes import PropTypes from'prop-types'; export default class Welcome extends React.Component { render() { return ( <h1>Hello, {this.props.name}</h1> ); } } Welcome.propTypes = { name: PropTypes.string.isRequired }; 169 https://facebook.github.io/react/docs/typechecking-with-proptypes.html