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

Quick Start to React - Update 2024-01-03

Quick Start to React - Update 2024-01-03

Includes ES6 & ESNext and Hooks.

Jussi Pohjolainen

January 03, 2024
Tweet

More Decks by Jussi Pohjolainen

Other Decks in Technology

Transcript

  1. Learning Path for FullStack Core EcmaScript Browser Host Objects HTML5

    CSS JavaScript Frameworks: React, Angular, Vue… 3 Core EcmaScript Node.js Databases / SQL Restful HTTP
  2. Learning Path for FullStack Core EcmaScript Browser Host Objects JavaScript

    Frameworks: React, Angular, Vue… 4 Java SE Spring Boot Databases / SQL Restful HTTP HTML5 CSS
  3. 5

  4. Different Libraries React AngularJS Angular 2+ JavaScript TypeScript Vue ...

    ... 6 It’s possible to do TypeScript also in other libraries..
  5. Topics • Tools • JSX and React Components • React

    lifecycle methods • Hooks: useState, useEffect, useContext • Conditional rendering • Lists and Keys • Routing with React Router • Api Calls: Fetch API and Axios 7
  6. create-react-app vs vite • React, JSX, ES6 • Webpack for

    bundling • Babel for transpiling • Autoprefixing CSS with PostCSS (browser compatibility) • ESLint for linting • Jest for testing • Development server for with hot reloading • Build scripts for production • React, JSX, ES6 • Rollup • Esbuild (can be used with SWC or Babel) • ESLint is an option • Jest is an option 9
  7. Tooling • CRA depricated! • Other choices (recommendations by FB)

    • Next.js (2016) • Remix • Gatsby • Learning curve for React maybe harder, from Next.js • To effectively use Next.js, it helps to be familiar with JavaScript, React, and related web development concepts. • So you should know React before learning Next.js! • Other alternative, maybe good for learning (2020): https://vitejs.dev 10
  8. 11 Feature/Aspect Next.js Remix Gatsby CRA Vite Framework Base React

    React React React Vanilla JS, React, Vue, etc. Server-Side Rendering Built-in Built-in Via plugins Not built-in Via plugins or SvelteKit Static Site Generation Built-in Built-in Built-in Manual (e.g., prerender) Via plugins or SvelteKit Client-Side Rendering Built-in Built-in Built-in Built-in Built-in Data Fetching getServerSideProps, getStaticProps load function in routes GraphQL, source plugins Fetch, Axios, etc. Fetch, Axios, etc. Routing File-system based File-system based File-system & GraphQL React Router, etc. File-system or manual API Support API routes in /api Route layouts & loaders Serverless Functions Manual setup Manual setup Styling Any CSS-in-JS or CSS/SASS Any CSS-in-JS or CSS/SASS Any CSS-in-JS or CSS/SASS Any CSS-in-JS or CSS/SASS Any CSS-in-JS or CSS/SASS Plugin Ecosystem Next.js plugins Mostly native React Rich Gatsby Plugin Ecosystem Limited to npm packages Vite plugins Community & Adoption Large Growing Large Very Large Growing Performance Optimization Automatic via Vercel Optimized out-of- the-box Optimized, especially for images Manual Fast (uses native ESM) Learning Curve Moderate Moderate to High Moderate Easy Moderate Use Case Universal (SSR, SSG, CSR) Universal (SSR, SSG, CSR) focus on fast navigation SSG: Mostly static site generation, good for blogs, docs CSR CSR, optimized for speed
  9. CRA: Client-Side Rendering • HTML generated in the browser. •

    JS handles the rendering of the UI in browser • Slower initial load; content fetched via JavaScript. • Search Engine Optimization less optimal, but improving. • Less server resource usage; more client CPU usage. 12
  10. Next.js: Server-Side Rendering (SSR) • It is hybrid: both SSR

    and CSR • Optionally: Server creates pre-rendered HTML and serves it to client • Client will have JS which will do interactivity • HTML generated on the server for each request. • Initial load faster; content immediately available. • SEO-friendly. • Requires server resources for rendering. 13
  11. CRA function App() { const [data, setData] = useState(null); useEffect(()

    => { axios.get('/api/data') .then(response => { setData(response.data); }); }, []); return ( <div> {data ? <h1>{data.message}</h1> : <p>Loading...</p>} </div> ); } 14 1. The page initially loads with a "Loading..." message. 2. An API request is made to /api/data. 3. Once data is received, it replaces "Loading..." with the actual data.
  12. Next.js export async function getServerSideProps() { const response = await

    axios.get('http://localhost:3000/api/data'); return { props: { data: response.data } }; } const HomePage = ({ data }) => { return ( <div> <h1>{data.message}</h1> </div> ); }; 15 1.Before the page is rendered, getServerSideProps fetches data from /api/data. 2.The data is passed as a prop to the component. 3.The server sends a fully rendered HTML page with the data included. => Faster initial load => Better search engine optimization
  13. CRA vs Vite • Performance: Vite is known for being

    faster than CRA in terms of both build time and development server startup time. • Ecosystem: CRA has a larger ecosystem and more community support, as it has been around for longer and is more widely used. • Features: CRA provides many features out of the box, including a development server, a production build script, and preconfigured ESLint and Babel settings. Vite, on the other hand, provides a minimalistic set of features out of the box and allows you to add additional plugins as needed. • Flexibility: Vite is more flexible than CRA, as it allows you to use different build tools and frameworks, not just React. Vite supports Vue.js, Svelte, and vanilla JavaScript, while CRA is specifically designed for React. 16
  14. <!DOCTYPE html> <html> <head> <title>Title</title> <meta charset="UTF-8" /> <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>

    <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script> </head> <body> <div id="root"></div> <script> var rootElement = React.createElement( "a", { href: "https://reactjs.org/" }, "React" ); const root = ReactDOM.createRoot(document.getElementById("root")); root.render(rootElement); </script> </body> </html> 18 Import needed JS files from cloud Modifying the page by using React
  15. 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> 19
  16. 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! 20
  17. <!DOCTYPE html> <html> <head> <title>Title</title> <meta charset="UTF-8" /> <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>

    <script src="https://unpkg.com/react-dom@18/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"> const root = ReactDOM.createRoot(document.getElementById("root")); root.render(<p>Hello</p>); </script> </body> </html> Let's import babel also JSX can be used Notice the script type! 21
  18. 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> 22
  19. Components • Class Components • Older, more verbose. • Lifecycle

    methods explicitly defined. • Requires this keyword for state and props. • Use if you need lifecycle methods like componentDidMount. • Functional Components • Functional Components • Modern, less verbose. • Hooks provide lifecycle and state features. • Easier to test and maintain. • Recommended for new projects. 24
  20. Recommendation • Functional components • New projects • simplicity, hooks,

    and community direction. • Class components • Legacy projects 25
  21. React version < v16.8 Function Component Class Component Properties X

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

    X State X (Hooks) X Life cycle methods X (Hooks) X 27 React Hooks enables same functionality
  23. 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 28
  24. 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 30
  25. 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 31
  26. 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. 32
  27. Using Arrays function App() { const a = <p>Hello World</p>;

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

    2 } const doIt2 = function() { return 1 + 2 } const doIt3 = () => { return 1 + 2 } const doIt4 = () => 1 + 2 34
  29. 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')) 35
  30. React version >= v16.8 Function Component Class Component Properties X

    X State X (Hooks) X Lifecycle methods X (Hooks) X 37
  31. 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 38
  32. 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') ); 39
  33. 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! 40
  34. 41

  35. 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... 42
  36. 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. 43
  37. 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! 44
  38. 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") 45
  39. 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 46
  40. 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. 47 You can replace this syntax with class fields!
  41. 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') ); 48 You can replace this syntax with class fields!
  42. Class Fields ESNext class Person { name = 'jack' }

    Transforms into class Person { constructor() { this.name = 'jack' } } 49
  43. 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) } } } 50
  44. 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) } } 51 Uses closures
  45. 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 53
  46. 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 54
  47. 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 55
  48. 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 56
  49. 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) }); 57
  50. 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 58
  51. 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 60
  52. 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 61
  53. 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! 62
  54. 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 63
  55. 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 64
  56. 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>); } } 65
  57. 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 66
  58. 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>); } } 67
  59. 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 68
  60. 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/ 69
  61. 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(.....); 70
  62. 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> ); } } 71
  63. 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> ); } } 72 With async functions you can use await Using await
  64. 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> ); } } 73
  65. 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> ); } } 74
  66. Optimizing handleChange handleChange = (event) => { // "name" or

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

    "age" let nameOfTheInput = event.target.name this.setState({[nameOfTheInput]: event.target.value}) } ES6: Computated property name 76
  68. render() • Most used lifecycle method and required • No

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

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

    • You can use setState, but beware of iternal loop! 80
  71. Main App component const App = () => <Character id={1}/>

    82 This custom component fetches star wars character name with id 1 (Uses Swapi)
  72. 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> } } 83 1) render 2) componentDidMount 3) done (triggers render again)
  73. 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> } } 84 Now user is asked the id. Fetching is done only once! Component is already mounted!
  74. 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> } } 85 This is called now several times! Only one time
  75. 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> } } 86 1) when component updates, fetching happens 2) fetching done, state changes, component updates again! 1) happens again!
  76. 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> } } 87 Trigger fetch only if props changed
  77. React version >= v16.8 Function Component Class Component Properties X

    X State X (Hooks) X Life cycle methods X (Hooks) X 89 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 90
  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> ); } 91 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> ); } 92 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> ); } 93 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> ); } 94 Button increases the count by one
  83. function App() { let [counter, setCounter] = useState(0); const add

    = () => { setCounter(counter + 1); setCounter(counter + 1); }; return ( <> <h1>Counter</h1> <p>{counter}</p> <button onClick={add}>+</button> </> ); } 95 Button increases the count still by one!
  84. function App() { let [counter, setCounter] = useState(0); const add

    = () => { setCounter(counter + 1); setCounter(counter + 1); }; return ( <> <h1>Counter</h1> <p>{counter}</p> <button onClick={add}>+</button> </> ); } 96 Passing function to a function…
  85. function App() { let [counter, setCounter] = useState(0); const add

    = () => { setCounter(counter + 1); // 0 + 1 setCounter(counter + 1); // 0 + 1 }; return ( <> <h1>Counter</h1> <p>{counter}</p> <button onClick={add}>+</button> </> ); } 97 Because of closure, we have 0 + 1 and 0 + 1!
  86. function App() { let [counter, setCounter] = useState(0); const add

    = () => { setTimeout(() => { setCounter(counter + 1); }, 1000); }; return ( <> <h1>Counter</h1> <p>{counter}</p> <button onClick={add}>+</button> </> ); } 98 Doing async stuff, clicking button several times. Triggers 0 + 1 several times, end result is 1
  87. 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> ); } 99 Passing a function and now it works!
  88. const App = () => { const [people, setPeople] =

    React.useState([]); const click = () => { for (let i = 1; i < 4; i++) { fetch(`https://swapi.dev/api/people/${i}/`) .then((hr) => hr.json()) .then((data) => { setPeople((prevPeople) => [...prevPeople, data]); }); } }; return ( <> <button onClick={click}>+</button> <ul> {people.map((person, index) => ( <li key={index}>{person.name}</li> ))} </ul> </> ); }; 100
  89. function App() { let [amount, setAmount] = useState(1); let [jokes,

    setJokes] = useState([]); const fetchIt = async () => { // set jokes to empty setJokes([]); for (let i = 0; i < amount; i++) { let hr = await fetch("https://api.chucknorris.io/jokes/random"); let data = await hr.json(); setJokes((prevJokes) => [...prevJokes, data.value]); } }; return ( <> <h1>Jokes</h1> <input onChange={(e) => setAmount(Number(e.target.value))} type="number" placeholder="amount" /> <button onClick={fetchIt}>Fetch {amount} jokes</button> <ul> {jokes.map((joke, index) => ( <li key={index}>{joke}</li> ))} </ul> </> ); } 101
  90. 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 102
  91. 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> } 103 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...
  92. 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> } 104 "dependencies", call effect function if depency value change. By giving empty array, it will be called only once
  93. 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> ); } 105 Function is called when resource is changed
  94. 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> ); } 106 Fetches stuff when resource changes!
  95. 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> } 107 The useEffect function returns a function...
  96. 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> ); } 108 When trying to use async, it won't work because async function returns a promise by default
  97. 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> ); } 109 Solution, create another function that is async
  98. import { useState, useEffect } from 'react'; const useFetch =

    (url) => { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchData = async () => { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const json = await response.json(); setData(json); setLoading(false); } catch (e) { setError(e); setLoading(false); } }; fetchData(); }, [url]); return { data, loading, error }; }; export default useFetch; 111
  99. import React from 'react'; import useFetch from './path-to-useFetch'; // Adjust

    the path as needed const MyComponent = () => { const { data, loading, error } = useFetch('https://api.example.com/data'); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return ( <div> {data && <div>{JSON.stringify(data)}</div>} </div> ); }; export default MyComponent; 112
  100. 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)); 114
  101. 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> } } 115 Notice the checking here that trigger the fetch only if props.id changed
  102. HTTP POST with Axios axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone'

    }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); 116
  103. 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> } 117 Changing the state will trigger useEffect again! Eternal loop!
  104. 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> } 118 Every time a props.id is changed trigger the effect
  105. 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 120
  106. Lists class List extends React.Component { render() { let items

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

    = ["a", "b"] let liItems = items.map((i) => <li>{i}</li>) return <ul>{liItems}</ul> } } 123
  108. 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 124
  109. 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> } } 125
  110. 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 127
  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] 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> } } 128 Generates random name This component wants to display the random name
  112. 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> } } 129 When button clicked random name is in console. How to pass this to DisplayRandomName component?
  113. 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> } } 130 .. And the function is passed here We have a function in the props now…
  114. 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> } } 131 Create state object from received name and send that to component
  115. import React from "react"; const MyButton = () => <button>Click</button>;

    const Toolbar = () => ( <div> <h1>Title</h1> <MyButton /> </div> ); const App = () => { return ( <div> <Toolbar /> </div> ); }; export default App; 133 When button presses, change button background to random color
  116. import { useState } from "react"; const MyButton = ()

    => { let [color, setColor] = useState("white"); const change = () => { let colors = ["hotpink", "pink", "yello", "green", "blue", "red"]; setColor(colors[Math.floor(Math.random() * colors.length)]); }; return ( <button style={{ backgroundColor: color }} onClick={() => change()}> Click </button> ); }; 134 color changes
  117. const MyButton = ({ clicked }) => { let [color,

    setColor] = useState("white"); const change = () => { let colors = ["hotpink", "pink", "yello", "green", "blue", "red"]; let color = colors[Math.floor(Math.random() * colors.length)]; setColor(color); clicked(color); }; return ( <button style={{ backgroundColor: color }} onClick={() => change()}> Click </button> ); }; const Toolbar = () => { let [color, setColor] = useState("white"); const clicked = (myColor) => { setColor(myColor); }; return ( <div> <h1>{color}</h1> <MyButton clicked={clicked} /> </div> ); }; 135 Lifting state up, now title also changes
  118. function ThemedButton({ theme }) { return <button style={{ backgroundColor: theme

    }}>Click</button>; } function Toolbar({ theme }) { return ( <div> <ThemedButton theme={theme} /> </div> ); } function App() { return <Toolbar theme="hotpink" />; } 136
  119. const ThemeContext = React.createContext(); function ThemedButton({ theme }) { const

    value = React.useContext(ThemeContext); return <button style={{ backgroundColor: value }}>Click</button>; } function Toolbar() { return ( <div> <ThemedButton /> </div> ); } function App() { return ( <ThemeContext.Provider value="yellow"> <Toolbar /> </ThemeContext.Provider> ); } 137
  120. import { useContext, createContext, useState } from "react"; const ColorContext

    = createContext(); const MyButton = () => { const [color, setColor] = useContext(ColorContext); const change = () => { let colors = ["hotpink", "pink", "yellow", "green", "blue", "red"]; let color = colors[Math.floor(Math.random() * colors.length)]; setColor(color); }; return ( <button style={{ backgroundColor: color }} onClick={() => change()}> Click </button> ); }; const Toolbar = () => { const [color] = useContext(ColorContext); return ( <div> <h1>{color}</h1> <MyButton /> </div> ); }; const App = () => { let [color, setColor] = useState("white"); return ( <ColorContext.Provider value={[color, setColor]}> <Toolbar /> </ColorContext.Provider> ); }; 138
  121. const App = () => { let [color, setColor] =

    useState("white"); return ( <ColorContext.Provider value={[color, setColor]}> <Toolbar /> </ColorContext.Provider> ); }; => const App = () => { return ( <ColorContextProvider> <Toolbar /> </ColorContextProvider> ); }; 139 Let's hide this stuff to it's own file Let's create our own component for this
  122. import React from "react"; const ColorContext = React.createContext(); function ColorContextProvider(props)

    { let [color, setColor] = React.useState("hotpink"); return ( <ColorContext.Provider value={[color, setColor]}> {props.children} </ColorContext.Provider> ); } export { ColorContextProvider, ColorContext }; 140
  123. import { useContext, useState } from "react"; import { ColorContextProvider,

    ColorContext } from "./ColorContext"; const MyButton = () => { const [color, setColor] = useContext(ColorContext); const change = () => { let colors = ["hotpink", "pink", "yellow", "green", "blue", "red"]; let color = colors[Math.floor(Math.random() * colors.length)]; setColor(color); }; return ( <button style={{ backgroundColor: color }} onClick={() => change()}> Click </button> ); }; const Toolbar = () => { const [color] = useContext(ColorContext); return ( <div> <h1>{color}</h1> <MyButton /> </div> ); }; const App = () => { return ( <ColorContextProvider> <Toolbar /> </ColorContextProvider> ); }; export default App; 141
  124. Tooling • npx create-react-app myapp • npx create-next-app myapp •

    npx create-remix myapp • npx create-gatsby 143
  125. CRA • Advantages: • Simplicity: Out-of-the-box setup for building a

    React app with no configuration. • Zero Config: Automatically handles bundling, linting, and other tasks. • Ejectable: Can "eject" to customize build scripts, but this is a one-way operation. • Regular Updates: Maintained by Facebook, ensuring compatibility with newer React features. • Disadvantages: • SPA Focused: Not optimized for server-side rendering or static site generation. • Limited Flexibility: Requires ejecting for advanced customizations, which can make the setup complex. • Larger Bundles: Without manual optimization, bundles might be larger compared to other tools. 144
  126. Next.js • Advantages: • Versatility: Supports server-side rendering, static site

    generation, and client-side rendering. • File-based Routing: Automatic route creation based on the pages directory. • Built-in API Routes: Simplifies backend API creation with Node.js. • Performance Optimizations: Automatic code-splitting and image optimization. • Developer Experience: Features like fast refresh enhance the development process. • Disadvantages: • Learning Curve: Requires understanding Next.js-specific life-cycle methods and features. • Configuration: While it's zero-config by default, custom setups (like with custom servers) can become complex. • Larger Node.js Server Footprint: If using server-side rendering features, deployment might need resources for a Node.js server. 145
  127. 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 146
  128. 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> 147
  129. Starting point: index.js import React from 'react'; import ReactDOM from

    'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> ); Inject <App/> where <div id="root"> is Strict mode enables additional warnings 149
  130. 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 151
  131. 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')); 152
  132. 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; 153