Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Quick Start to React

Quick Start to React

Includes ES6 & ESNext and Hooks.

D9e65f4b0af059ae9ba243c8c2265e4f?s=128

Jussi Pohjolainen

May 25, 2020
Tweet

Transcript

  1. Quick Start to React Jussi Pohjolainen 1

  2. Learning Path for Front-end Core EcmaScript Browser Host Objects JavaScript

    in Front-end HTML5 CSS Static Web-pages JavaScript Frameworks: React, Angular, Vue… 2
  3. Tips • Learn modern ECMAScript (ES6+) first! • ECMAScript is

    "totally" different language than some others • It does not hurt if you know also ES5.. • Learn HTTP and RESTful API • HTTP GET/POST/DELETE etc • Also vanilla JS browser development gives you a good understanding of frontend development 3
  4. Different Libraries React AngularJS Angular 2+ JavaScript TypeScript Vue ...

    ... 4 It’s possible to do TypeScript also in other libraries..
  5. React vs Angular React • Pure React is just for

    UI • JavaScript preference • JSX for templates • One-way databinding Angular 2+ • In addition for UI you will have lot of other libraries for AJAX, services, modules ... • TypeScript preference • Strong opinions how app should be structured • HTML for templates with add-ons from Angular • Two-way databinding • Learning curve can be steep 5
  6. Hello World 6

  7. <!DOCTYPE html> <html> <head> <title>Title</title> <meta charset="UTF-8" /> <script crossorigin

    src="https://unpkg.com/react@16/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script> </head> <body> <div id="root"></div> <script> var rootElement = React.createElement('a', {href: 'https://reactjs.org/'}, "React"); ReactDOM.render(rootElement, document.getElementById('root')) </script> </body> </html> 7 Import needed JS files from cloud Modifying the page by using React
  8. Using Babel • Babel will compile your app to older

    EcmaScript that browsers understand • Also Babel understands JSX which is heavily used in React • JSX is an extension to JavaScript • Easiest way to get babel is just to add • <script src="https://unpkg.com/babel- standalone@6/babel.min.js"></script> 8
  9. JSX? // JSX let element = <h1>Hello, world!</h1>; // Babel

    compiles this to var element = React.createElement( "h1", null, "Hello, world!" ); No " or ' chars here! This is NOT a string! 9
  10. <!DOCTYPE html> <html> <head> <title>Title</title> <meta charset="UTF-8" /> <script crossorigin

    src="https://unpkg.com/react@16/umd/react.production.min.js"></script> <script crossorigin src="https://unpkg.com/react-dom@16/umd/react- dom.production.min.js"></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> </head> <body> <div id="root"></div> <script type="text/babel"> ReactDOM.render(<h1>Hello</h1>, document.getElementById('root')) </script> </body> </html> Let's import babel also JSX can be used Notice the script type! 10
  11. Expressions in JSX • You can embed any JS expression

    inside of JSX • For example • 2 + 2, location.latitude, doIt(user) • To do this add { } in JSX • let jsx = <h1>Hello { doIt() }</h1> 11
  12. Components 12

  13. Components • React application is built using components • Component

    can be • Function Component • Class Component 13
  14. React version < v16.8 Function Component Class Component Properties X

    X State X Life cycle methods X 14 If you want state and life cycle, classes are you choice!
  15. React version >= v16.8 Function Component Class Component Properties X

    X State X (Hooks) X Life cycle methods X (Hooks) X 15 React Hooks enables same functionality
  16. No Breaking Changes • There are no plans to remove

    classes from React • You can implement your app using classes or functions or mix • It seems that trend is towards functional components 16
  17. Function Components 17

  18. Function Components • Typically you define the JSX inside of

    a component • A component let's you split the UI into reusable pieces. The component can be • Function component • Class component • To declare a function component • Function is written with capital letter • Returns react element • Usually accepts object argument 18
  19. Example // function component function Link(properties) { return <a href={properties.link}>{properties.text}</a>;

    } ReactDOM.render( <Link link="http://www.tuni.fi" text="tuni"/>, document.getElementById('root') ); properties = {"link": "http://www.tuni.fi", "text": "uta"} properties are read only, do not try to modify them 19
  20. function Link(properties) { return <a href={properties.link}>{properties.text}</a>; } function T3() {

    return ( <ul> <li><Link link="http://www.uta.fi" text="uta"/></li> <li><Link link="http://www.tut.fi" text="tut"/></li> <li><Link link="http://www.tamk.fi" text="tamk"/></li> </ul> ); } ReactDOM.render( <T3/>, document.getElementById('root') ); Typically React apps have one root element. 20
  21. Using Arrays function App() { const a = <p>Hello World</p>;

    return <div>{[a,a,a,a]}</div>; } ReactDOM.render( <App/>, document.getElementById('root') ); 21
  22. ES6+ Using Arrow Functions function doIt1() { return 1 +

    2 } const doIt2 = function() { return 1 + 2 } const doIt3 = () => { return 1 + 2 } const doIt4 = () => 1 + 2 22
  23. Arrow Functions and JSX const Link = (properties) => <a

    href={properties.link}>{properties.text}</a> const T3 = () => ( <ul> <li><Link link="http://www.uta.fi" text="uta"/></li> <li><Link link="http://www.tut.fi" text="tut"/></li> <li><Link link="http://www.tamk.fi" text="tamk"/></li> </ul> ) ReactDOM.render(<T3/>, document.getElementById('root')) 23
  24. Component Classes 24

  25. React version >= v16.8 Function Component Class Component Properties X

    X State X (Hooks) X Life cycle methods X (Hooks) X 25 Let's see first how life cycle and state are implemented using classes
  26. Component Classes function Welcome(props) { return <h1>Hello, {props.name}</h1>; } ó

    class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } } Use this.props to access the properties 26
  27. Lifecycle methods class Clock extends React.Component { // When object

    is created from Clock constructor(props) { super(props); console.log("constructor") } // When Clock is rendered to the DOM componentDidMount() { console.log("componentDidMount") } // When Clock is removed from the DOM componentWillUnmount() { console.log("componentWillUnmount") } render() { console.log("render") let time = new Date().toLocaleTimeString() return (<div>{time}</div>); } } ReactDOM.render( <Clock />, document.getElementById('root') ); 27
  28. class Clock extends React.Component { constructor(props) { super(props); this.tick(); }

    componentDidMount() { // invoke this.tick for every 1 sec this.timer = setInterval(this.tick, 1000); } tick() { this.time = new Date().toLocaleTimeString() } componentWillUnmount() { clearInterval(this.timer); } render() { console.log("render") return (<div>{this.time}</div>); } } ReactDOM.render( <Clock />, document.getElementById('root') ); 1) will call tick() 2) tick will initalize time 3) Clock is rendered with time 4) Will set interval and invoke tick() for every 1 sec 5) Called for every 1 sec NOT working! Clock is not updating! 28
  29. 29

  30. Problem 1 componentDidMount() { // invoke this.tick for every 1

    sec this.timer = setInterval(this.tick, 1000); } tick() { console.log(this); this.time = new Date().toLocaleTimeString() } this is window! It's not the clock object! So in here we are creating a global variable... 30
  31. Solution: Closure componentDidMount() { let _this = this this.timer =

    setInterval(function() { _this.tick() }, 1000); } Closure: When inner function accesses outer functions variables, those variables stay in memory. 31
  32. Solution: Arrow Function componentDidMount() { // invoke this.tick for every

    1 sec this.timer = setInterval(() => { this.tick() }, 1000); } If using arrow function, it does automatically the closure for "this". Arrow function contains "lexical this" feature! 32
  33. Solution: Function Binding componentDidMount() { // invoke this.tick for every

    1 sec this.timer = setInterval(this.tick.bind(this), 1000); } The bind will create copy of the original function and replaces the "this" in the function with the given object, which in this case is the component object ("this") 33
  34. class Clock extends React.Component { constructor(props) { super(props); this.tick =

    this.tick.bind(this) this.tick(); } componentDidMount() { // invoke this.tick for every 1 sec this.timer = setInterval(this.tick, 1000); } tick() { this.time = new Date().toLocaleTimeString() } componentWillUnmount() { clearInterval(this.timer); } render() { console.log("render") return (<div>{this.time}</div>); } } ReactDOM.render( <Clock />, document.getElementById('root') ); Replaces the tick function with a function that does not containg this keyword, instead this keyword is replaced with Clock object 34
  35. class Clock extends React.Component { constructor(props) { super(props); this.tick =

    () => { this.time = new Date().toLocaleTimeString() } this.tick(); } componentDidMount() { // invoke this.tick for every 1 sec this.timer = setInterval(this.tick, 1000); } tick() { this.time = new Date().toLocaleTimeString() } componentWillUnmount() { clearInterval(this.timer); } render() { console.log("render") return (<div>{this.time}</div>); } } ReactDOM.render( <Clock />, document.getElementById('root') ); If we move the function from Clock.prototype to Clock object itself which uses arrow syntax (lexical this), it will work also. 35 You can replace this syntax with class fields!
  36. class Clock extends React.Component { constructor(props) { super(props); this.tick =

    () => { this.time = new Date().toLocaleTimeString() } this.tick(); } componentDidMount() { // invoke this.tick for every 1 sec this.timer = setInterval(this.tick, 1000); } tick = () => { this.time = new Date().toLocaleTimeString() } componentWillUnmount() { clearInterval(this.timer); } render() { console.log("render") return (<div>{this.time}</div>); } } ReactDOM.render( <Clock />, document.getElementById('root') ); 36 You can replace this syntax with class fields!
  37. Class Fields ESNext class Person { name = 'jack' }

    Transforms into class Person { constructor() { this.name = 'jack' } } 37
  38. Class Fields ESNext class Person { name = 'jack' printName

    = () => { console.log(this.name) } } ES6 class Person { constructor() { this.name = 'jack' this.printName = () => { console.log(this.name) } } } 38
  39. Lexical this in arrow functions ES6 class Person { constructor()

    { this.name = 'jack' this.printName = () => { console.log(this.name) } } } ES5 function Person() { var _this = this this.name = 'jack' this.printName = function() { console.log(_this.name) } } 39 Uses closures
  40. 40 https://reactjs.org/docs/hooks-intro.html

  41. class Clock extends React.Component { constructor(props) { super(props); let stateObject

    = {time: new Date().toLocaleTimeString()} this.state = stateObject } componentDidMount() { this.timer = setInterval(this.tick, 1000); } tick = () => { let stateObject = {time: new Date().toLocaleTimeString()} this.setState(stateObject) } componentWillUnmount() { clearInterval(this.timer); } render() { return (<div>{this.state.time}</div>); } } ReactDOM.render( <Clock />, document.getElementById('root') ); The component has a state When changing the state... ... render is called 41
  42. Using the State • Do not modify the state directly

    (you can do this only in constructor) • this.state.time = "something" • Instead of use the setState • this.setState({time: "something"}) • Setting the state may be asynchronous • State updates are merged • State should be capsulated into the component • By using props, state can be given to child component 42
  43. class Clock extends React.Component { constructor(props) { super(props); let stateObject

    = {time: new Date().toLocaleTimeString(), place: "Helsinki"} this.state = stateObject } componentDidMount() { this.timer = setInterval(this.tick, 1000); } tick = () => { let stateObject = {time: new Date().toLocaleTimeString()} this.setState(stateObject) } componentWillUnmount() { clearInterval(this.timer); } render() { return (<div>{this.state.time} {this.state.place}</div>); } } When setting new state with "time" the "place" is still intact! This is merging 43
  44. class Clock extends React.Component { state = { time: new

    Date().toLocaleTimeString(), place: "Helsinki" } componentDidMount() { this.timer = setInterval(this.tick.bind(this), 1000); } tick = () => { let stateObject = { time: new Date().toLocaleTimeString() }; this.setState(stateObject); } componentWillUnmount() { clearInterval(this.timer); } render() { return ( <div> {this.state.time} {this.state.place} </div> ); } } You can also use ESNext with class fields for state and omit the constructor 44
  45. Fetch API • Fetch API is a standard (non react)

    API for AJAX • You can easily combine Fetch and React • Fetch API is asynchronous and uses Promises: fetch(url).then((response) => { return response.json() } ) .then((jsonObject) => { console.log(jsonObject) }); 45
  46. class Character extends React.Component { BASE_URL = "https://swapi.co/api/people/"; state =

    { characterData: { name: "", height: "" } }; componentDidMount() { let id = this.props.id; let url = `${this.BASE_URL}${id}/`; fetch(url) .then(resp => resp.json()) .then(this.update); } update = character => { let characterData = { name: character.name, height: character.height }; let stateObj = { characterData: characterData }; this.setState(stateObj); }; render() { return ( <p> name = {this.state.characterData.name}, height ={" "} {this.state.characterData.height} </p> ); } } Public API for fetching Star Wars data Fetching data and changing state 46
  47. Forms and Handling Events 47

  48. Handling Events Example class Button extends React.Component { buttonClicked(event) {

    console.log('click') } render() { return (<button onClick={this.buttonClicked}>Click</button>); } } Calling the buttonClicked Notice camelCase! event (SyntheticEvent) brings you information about the event 48
  49. Using Event class Link extends React.Component { linkClicked(event) { event.preventDefault();

    console.log('click') } render() { return (<a onClick={this.linkClicked} href="">Link</a>); } } Preventing the default behaviour link usually opens another page 49
  50. Using this class Link extends React.Component { linkClicked(event) { event.preventDefault();

    console.log(this) } render() { return (<a onClick={this.linkClicked} href="">Link</a>); } } It's NOT referring to object of Link – class! 50
  51. And once again with the this - problem class Link

    extends React.Component { constructor(props) { super(props) this.linkClicked = this.linkClicked.bind(this) } linkClicked(event) { event.preventDefault(); console.log(this) } render() { return (<a onClick={this.linkClicked} href="">Link</a>); } } Now this refers to Link object in the linkClicked - method 51
  52. Solution: Binding in function call class Link extends React.Component {

    constructor(props) { super(props) } linkClicked(event) { event.preventDefault(); console.log(this) } render() { return (<a onClick={this.linkClicked.bind(this)} href="">Link</a>); } } Now this refers to Link object in the linkClicked - method 52
  53. Solution: Binding in constructor class Link extends React.Component { constructor(props)

    { super(props) this.linkClicked = this.linkClicked.bind(this) } linkClicked(event) { event.preventDefault(); console.log(this) } render() { return (<a onClick={this.linkClicked} href="">Link</a>); } } 53
  54. Solution: Using Arrow Functions class Link extends React.Component { constructor(props)

    { super(props) } linkClicked(event) { event.preventDefault(); console.log(this) } render() { return (<a onClick={(event) => { this.linkClicked(event) }} href="">Link</a>); } } Now this refers to Link object in the linkClicked - method 54
  55. Solution: Using Arrow Functions class Link extends React.Component { constructor(props)

    { super(props) } linkClicked = () => (event) { event.preventDefault(); console.log(this) } render() { return (<a onClick={this.linkClicked} href="">Link</a>); } } 55
  56. Using State class Link extends React.Component { state = {stuff:

    ’’} linkClicked = (event) => { event.preventDefault(); this.setState({stuff: 'clicked'}) } render() { return (<div><a onClick={this.linkClicked} href="">Link</a> <p>{this.state.stuff}</p></div>); } } Will update the view when state changes 56
  57. Using Backend: locations • Cloning backend for testing purposes •

    git clone https://github.com/pohjus/restful-api-nodejs • cd restful-api-nodejs/ • node app.js • Testing backend • Fetch all locations • curl http://localhost:8080/locations/ • Fetch one location • curl http://localhost:8080/locations/1 • Add one location • curl -d "{\"lat\": 80, \"lon\": 80}" -H "Content-type: application/json" http://localhost:8080/locations/ 57
  58. Using Fetch API on HTTP POST let location = {

    lat: 50, lon: 50 }; let conf = { method: "POST", body: JSON.stringify(location), headers: { "Content-type": "application/json" } }; fetch("http://localhost:8080/locations", conf).then(.....); 58
  59. class LocationPost extends React.Component { state = { success: ""

    }; linkClicked = event => { event.preventDefault(); let location = { lat: 50, lon: 50 }; let conf = { method: "POST", body: JSON.stringify(location), headers: { "Content-type": "application/json" } }; fetch("http://localhost:8080/locations", conf).then(this.done); }; done = httpResponse => { this.setState({ success: httpResponse.status === 201 }); }; render() { let text = ""; if (typeof this.state.success === "boolean") { text = this.state.success ? "Added new Location" : "Problem Adding"; } return ( <div> <a onClick={this.linkClicked} href=""> Add Location </a> <p>{text}</p> </div> ); } } 59
  60. class LocationPost extends React.Component { state = { success: ""

    }; linkClicked = async (event) => { event.preventDefault(); let location = { lat: 50, lon: 50 }; let conf = { method: "POST", body: JSON.stringify(location), headers: { "Content-type": "application/json" } }; let httpResponse = await fetch("http://localhost:8080/locations", conf) this.setState({ success: httpResponse.status === 201 }); }; render() { let text = ""; if (typeof this.state.success === "boolean") { text = this.state.success ? "Added new Location" : "Problem Adding"; } return ( <div> <a onClick={this.linkClicked} href=""> Add Location </a> <p>{text}</p> </div> ); } } 60 With async functions you can use await Using await
  61. Using Forms class NameForm extends React.Component { state = {name:

    ''}; handleChange = (event) => { let userGivenText = event.target.value this.setState({name: userGivenText}); } showAlert = (event) => { event.preventDefault(); alert('A name was submitted: ' + this.state.name); } render() { return ( <div> <input placeholder="Name" type="text" onChange={this.handleChange} /> <button onClick={this.showAlert}>Display Alert</button> <p>Given name = {this.state.name}</p> </div> ); } } 61
  62. class NameForm extends React.Component { state = {name: '', age:

    ''}; handleChange = (event) => { // "name" or "age" let nameOfTheInput = event.target.name switch(nameOfTheInput) { case "name": this.setState({name: event.target.value}); break; case "age": this.setState({age: event.target.value}); break; } } render() { return ( <div> <input placeholder="Name" type="text" name="name" onChange={this.handleChange} /> <input placeholder="Age" type="text" name="age" onChange={this.handleChange} /> <p>Given name = {this.state.name}</p> <p>Given age = {this.state.age}</p> </div> ); } } 62
  63. Optimizing handleChange handleChange = (event) => { // "name" or

    "age" let nameOfTheInput = event.target.name let newState = {} newState[nameOfTheInput] = event.target.value this.setState(newState) } 63
  64. Optimizing handleChange handleChange = (event) => { // "name" or

    "age" let nameOfTheInput = event.target.name this.setState({[nameOfTheInput]: event.target.value}) } ES6: Computated property name 64
  65. More About Lifecycle 65

  66. render() • Most used lifecycle method and required • No

    side effects! • do not set state 66
  67. componentDidMount() • componentDidMount is called after render and when component

    is ready • You can use setState • Good place to initiative API calls 67
  68. componentDidUpdate() • If prop or state changes, componentDidUpdate is called

    • You can use setState, but beware of iternal loop! 68
  69. Example of Lifecycle 69

  70. Main App component const App = () => <Character id={1}/>

    70 This custom component fetches star wars character name with id 1 (Uses Swapi)
  71. Character.js class Character extends React.Component { state = {name: 'loading...'}

    componentDidMount() { fetch(`https://swapi.dev/api/people/${this.props.id}/`) .then(hr => hr.json()) .then(this.done) } done = (characterObject) => { this.setState({name: characterObject.name}) } render() { return <p>{this.state.name}</p> } } 71 1) render 2) componentDidMount 3) done (triggers render again)
  72. Modification to App.js class App extends React.Component { state =

    {id: 1} change = (event) => { this.setState({id: event.target.value}) } render() { return <div> <input type="number" placeholder="id" onChange={this.change}/> <Character id={this.state.id}/> </div> } } 72 Now user is asked the id. Fetching is done only once! Component is already mounted!
  73. Modification to Character.js class Character extends React.Component { state =

    {name: ''} componentDidMount() { console.log('mount') } componentDidUpdate(prevProps, prevStat) { console.log('update') } render() { return <p>{this.state.name}</p> } } 73 This is called now several times! Only one time
  74. Adding Fetch class Character extends React.Component { state = {name:

    ''} componentDidMount() { console.log('mount') } componentDidUpdate(prevProps, prevState) { fetch(`https://swapi.dev/api/people/${this.props.id}/`) .then(hr => hr.json()) .then(this.done) } done = (characterObject) => { console.log('done') this.setState({name: characterObject.name}) } render() { console.log('render') return <p>{this.state.name}</p> } } 74 1) when component updates, fetching happens 2) fetching done, state changes, component updates again! 1) happens again!
  75. Solution class Character extends React.Component { state = {name: ''}

    componentDidMount() { console.log('mount') } componentDidUpdate(prevProps, prevState) { if(this.props.id !== prevProps.id) { fetch(`https://swapi.dev/api/people/${this.props.id}/`) .then(hr => hr.json()) .then(this.done) } } done = (characterObject) => { this.setState({name: characterObject.name}) } render() { return <p>{this.state.name}</p> } } 75 Trigger fetch only if props changed
  76. React Hooks 76

  77. React version >= v16.8 Function Component Class Component Properties X

    X State X (Hooks) X Life cycle methods X (Hooks) X 77 Let's now focus on hooks
  78. Motivation • Sharing stateful logic between can be pain •

    Lifecycle method usage can be inconsistant • Classes can be difficult (this-keyword) • Hooks: All React Features without Classes 78
  79. import React from "react"; export default function App() { const

    arr = React.useState("initial text"); const text = arr[0]; const setText = arr[1]; return ( <div> <p>{text}</p> <button onClick={() => setText("hello")}>Click me</button> </div> ); } 79 Will return a array containing initial state value and a function for updating state When calling the function the state changes and rerender happens
  80. import { useState } from "react"; export default function App()

    { const [text, setText] = useState("initial text"); return ( <div> <p>{text}</p> <button onClick={() => setText("hello")}>Click me</button> </div> ); } 80 Object destruct Array destruct
  81. import { useState } from "react"; export default function App()

    { const [number1, setNumber1] = useState(0); const [number2, setNumber2] = useState(0); return ( <div> <p>{number1 + number2}</p> <input type="number" onChange={(e) => setNumber1(Number(e.target.value))} placeholder="number 1" /> <input type="number" onChange={(e) => setNumber2(Number(e.target.value))} placeholder="number 2" /> </div> ); } 81 Multiple states
  82. import { useState } from "react"; export default function App()

    { const [counter, setCounter] = useState(0); return ( <div> <p>{counter}</p> <button onClick={() => setCounter(counter + 1)}>+</button> </div> ); } 82 Button increases the count by one
  83. import { useState } from "react"; export default function App()

    { const [counter, setCounter] = useState(0); return ( <div> <p>{counter}</p> <button onClick={() => { setCounter(counter + 1); setCounter(counter + 1); }} > + </button> </div> ); } 83 Button increases the count still by one!
  84. import { useState } from "react"; export default function App()

    { const [counter, setCounter] = useState(0); return ( <div> <p>{counter}</p> <button onClick={() => { setCounter((prevCount) => prevCount + 1); setCounter((prevCount) => prevCount + 1); }} > + </button> </div> ); } 84 Passing a function and now it works!
  85. Effect Hooks (Lifecycle methods) • Classes contain lifecycle methods like

    componentDidMount, componentWillUnmount • By using Effect Hooks you can implement the functionality of these • You can do also more stuff with it using "dependencies" • Effect will react when some state value changes 85
  86. function Clock() { const [time, setTime] = React.useState(new Date().toString()) function

    update() { setTime(new Date().toString()) } React.useEffect(() => { let interval = setInterval(update, 1000) return () => clearInterval(interval) }) return <p>{time}</p> } 86 useEffect is similar to didMount AND didUpdate, happens after render If this function returns a function it is called when Clock is cleared from dom this will change state, useEffect called again...
  87. function Clock() { const [time, setTime] = React.useState(new Date().toString()) function

    update() { setTime(new Date().toString()) } React.useEffect(() => { let interval = setInterval(update, 1000) return () => clearInterval(interval) }, []) return <p>{time}</p> } 87 "dependencies", call effect function if depency value change. By giving empty array, it will be called only once
  88. import { useState, useEffect } from "react"; export default function

    App() { const [resource, setResource] = useState("posts"); useEffect(() => { console.log("TRIGGER"); }, [resource]); return ( <div> <p>{resource}</p> <button onClick={() => setResource("posts")}>posts</button> <button onClick={() => setResource("todos")}>todos</button> </div> ); } 88 Function is called when resource is changed
  89. import { useState, useEffect } from "react"; export default function

    App() { const [resource, setResource] = useState("posts"); useEffect(() => { fetch(`https://jsonplaceholder.typicode.com/${resource}`) .then((hr) => hr.json()) .then((data) => console.log(data)); }, [resource]); return ( <div> <p>{resource}</p> <button onClick={() => setResource("posts")}>posts</button> <button onClick={() => setResource("todos")}>todos</button> </div> ); } 89 Fetches stuff when resource changes!
  90. function Clock() { const [time, setTime] = React.useState(new Date().toString()) function

    update() { setTime(new Date().toString()) } React.useEffect(() => { let interval = setInterval(update, 1000) return () => clearInterval(interval) }, []) return <p>{time}</p> } 90 The useEffect function returns a function...
  91. import { useState, useEffect } from "react"; export default function

    App() { const [resource, setResource] = useState("posts"); useEffect(async () => { fetch(`https://jsonplaceholder.typicode.com/${resource}`) .then((hr) => hr.json()) .then((data) => console.log(data)); }, [resource]); return ( <div> <p>{resource}</p> <button onClick={() => setResource("posts")}>posts</button> <button onClick={() => setResource("todos")}>todos</button> </div> ); } 91 When trying to use async, it won't work because async function returns a promise by default
  92. import { useState, useEffect } from "react"; export default function

    App() { const [resource, setResource] = useState("posts"); useEffect(() => { const myFetch = async () => { let hr = await fetch(`https://jsonplaceholder.typicode.com/${resource}`); let data = await hr.json(); console.log(data); }; myFetch(); }, [resource]); return ( <div> <p>{resource}</p> <button onClick={() => setResource("posts")}>posts</button> <button onClick={() => setResource("todos")}>todos</button> </div> ); } 92 Solution, create another function that is async
  93. Sidenote: Using Axios 93

  94. Fetch API vs Axios • Fetch API (standard): • fetch(url).then(hr

    => hr.json()).then(data => console.log(data)) • Axios API (npm install axios): • axios.get(url).then(hr => console.log(hr.data)); 94
  95. Example import axios from 'axios'; class Character extends React.Component {

    state = {name: ''} componentDidUpdate(prevProps, prevState) { if(this.props.id !== prevProps.id) { axios.get(`https://swapi.dev/api/people/${this.props.id}/`) .then(resp => this.setState({name: resp.data.name})) } } render() { return <p>{this.state.name}</p> } } 95 Notice the checking here that trigger the fetch only if props.id changed
  96. HTTP POST with Axios axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone'

    }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); 96
  97. Using Axios function Character(props) { let [character, setCharacter] = React.useState({name:

    ''}) // Similar to componentDidMount AND componentDidUpdate React.useEffect(() => { axios.get(`https://swapi.dev/api/people/${props.id}/`).then(hr => { setCharacter(hr.data) }) }) console.log('render: ' + new Date().toString()) return <p>{character.name}</p> } 97 Changing the state will trigger useEffect again! Eternal loop!
  98. Preventing eternal loop function Character(props) { let [character, setCharacter] =

    React.useState({name: ''}) // Similar to componentDidMount AND componentDidUpdate React.useEffect(() => { axios.get(`https://swapi.dev/api/people/${props.id}/`).then(hr => { setCharacter(hr.data) }) }, [props.id]) console.log('render: ' + new Date().toString()) return <p>{character.name}</p> } 98 Every time a props.id is changed trigger the effect
  99. Conditional Rendering 99

  100. class Hello extends React.Component { render() { return <p>Hello!</p> }

    } class AccessDenied extends React.Component { render() { return <p>AccessDenied</p> } } class Login extends React.Component { render() { if(this.props.name === "Jack") return <Hello/> else return <AccessDenied/> } } ReactDOM.render( <Login name="Phil"/>, document.getElementById('root') ); Determining which component is returned 100
  101. Lists and Keys 101

  102. Lists class List extends React.Component { render() { let items

    = [<li>a</li>, <li>b</li>] return <ul>{items}</ul> } } 102
  103. Lists class List extends React.Component { render() { let items

    = ["a", "b"] let liItems = items.map((i) => <li>{i}</li>) return <ul>{liItems}</ul> } } 103
  104. Keys • Keys help React to identify which items have

    changed, added or removed (performance gain) • Keys should be given to the array elements • Key should be unique value in siblings 104
  105. Key Usage class List extends React.Component { render() { let

    items = [{id: 1, name: "Jack"}, {id: 2, name: "Anna"}, {id: 3, name: "Tina"}] let liItems = items.map((person) => <li key={person.id}>{person.name}</li>) return <ul>{liItems}</ul> } } 105
  106. Lifting State Up 106

  107. Lifting State Up • Several components share the same data

    • The data and state could be handled by parent component • When something happens in child component, invoke method in parent 107
  108. class RandomGenerator extends React.Component { generate = () => {

    let names = ['jack', 'tina', 'paul', 'susanne'] let index = Math.floor(Math.random() * names.length) let randomName = names[index] console.log(randomName) } render() { return <button onClick={this.generate}>Generate Random Name</button> } } class DisplayRandomName extends React.Component { render() { return <p>Display Random Name</p> } } class Parent extends React.Component { render() { return <div><RandomGenerator/><DisplayRandomName/></div> } } 108 Generates random name This component wants to display the random name
  109. class RandomGenerator extends React.Component { generate = () => {

    let names = ['jack', 'tina', 'paul', 'susanne'] let index = Math.floor(Math.random() * names.length) let randomName = names[index] console.log(randomName) } render() { return <button onClick={this.generate}>Generate Random Name</button> } } class DisplayRandomName extends React.Component { render() { return <p>Display Random Name</p> } } class Parent extends React.Component { render() { return <div><RandomGenerator/><DisplayRandomName/></div> } } 109 When button clicked random name is in console. How to pass this to DisplayRandomName component?
  110. class RandomGenerator extends React.Component { generate = () => {

    let names = ['jack', 'tina', 'paul', 'susanne'] let index = Math.floor(Math.random() * names.length) let randomName = names[index] this.props.buttonClicked(randomName) } render() { return <button onClick={this.generate}>Generate Random Name</button> } } class DisplayRandomName extends React.Component { render() { return <p>Display Random Name</p> } } class Parent extends React.Component { clicked = (name) => { console.log(name) } render() { return <div><RandomGenerator buttonClicked={this.clicked}/><DisplayRandomName/></div> } } 110 .. And the function is passed here We have a function in the props now…
  111. class RandomGenerator extends React.Component { generate = () => {

    let names = ['jack', 'tina', 'paul', 'susanne'] let index = Math.floor(Math.random() * names.length) let randomName = names[index] this.props.buttonClicked(randomName) } render() { return <button onClick={this.generate}>Generate Random Name</button> } } class DisplayRandomName extends React.Component { render() { return <p>Display Random Name: {this.props.name}</p> } } class Parent extends React.Component { state = {name: undefined} clicked = (name) => { this.setState({name}) // {name: 'tina'} } render() { return <div><RandomGenerator buttonClicked={this.clicked}/> <DisplayRandomName name={this.state.name}/></div> } } 111 Create state object from received name and send that to component
  112. Webpack and Babel 112

  113. Webpack and Babel: create-react-app • Usually you combine React with

    Webpack and Babel • Webpack will help you with ECMAScript modules • Babel helps you to compile ES6 (and JSX) to older JS so it works on different browsers • To combine react, webpack and babel, use create-react-app • npx create-react-app my-app • cd my-app • npm start • And in the end (to compile for production) • npm run build 113
  114. index.html in react project <body> <noscript> You need to enable

    JavaScript to run this app. </noscript> <div id="root"></div> <!-- This HTML file is a template. If you open it directly in the browser, you will see an empty page. You can add webfonts, meta tags, or analytics to this file. The build step will place the bundled scripts into the <body> tag. To begin the development, run `npm start` or `yarn start`. To create a production bundle, use `npm run build` or `yarn build`. --> </body> 114
  115. In Browser Webpack will create this 115

  116. Starting point: 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(); Inject <App/> where <div id="root"> is 116
  117. 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; 117
  118. React Router 118

  119. Routing • Change the view of the application according url

    • There is no official routing package made by FB • React Router (v4) is one of the most used ones. • For browser install • npm install react-router-dom 119
  120. index.js import React from 'react'; import ReactDOM from 'react-dom'; import

    { BrowserRouter } from 'react-router-dom' import App from './App'; ReactDOM.render(<BrowserRouter><App /></BrowserRouter>, document.getElementById('root')); 120
  121. App.js import React, { Component } from 'react'; import {

    Route, Link } from 'react-router-dom'; const Page1 = () => ( <h2>Page 1</h2> ) const Page2 = () => ( <h2>Page 2</h2> ) const Welcome = () => ( <h2>Welcome</h2> ) class App extends Component { render() { return ( <div> <h1>Routing Example</h1> <ul> <li><Link to="/page1">Page 1</Link></li> <li><Link to="/page2">Page 2</Link></li> </ul> <Route exact={true} path="/" component={Welcome}/> <Route path="/page1" component={Page1}/> <Route path="/page2" component={Page2}/> </div> ); } } export default App; 121