Slide 1

Slide 1 text

Quick Start to React Jussi Pohjolainen 1

Slide 2

Slide 2 text

Intro 2

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

5

Slide 6

Slide 6 text

Different Libraries React AngularJS Angular 2+ JavaScript TypeScript Vue ... ... 6 It’s possible to do TypeScript also in other libraries..

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Tools 8

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

CRA function App() { const [data, setData] = useState(null); useEffect(() => { axios.get('/api/data') .then(response => { setData(response.data); }); }, []); return (
{data ?

{data.message}

:

Loading...

}
); } 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.

Slide 15

Slide 15 text

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 (

{data.message}

); }; 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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

Hello World 17

Slide 18

Slide 18 text

Title
var rootElement = React.createElement( "a", { href: "https://reactjs.org/" }, "React" ); const root = ReactDOM.createRoot(document.getElementById("root")); root.render(rootElement); 18 Import needed JS files from cloud Modifying the page by using React

Slide 19

Slide 19 text

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 • 19

Slide 20

Slide 20 text

JSX? // JSX let element =

Hello, world!

; // Babel compiles this to var element = React.createElement( "h1", null, "Hello, world!" ); No " or ' chars here! This is NOT a string! 20

Slide 21

Slide 21 text

Title
const root = ReactDOM.createRoot(document.getElementById("root")); root.render(<p>Hello</p>); Let's import babel also JSX can be used Notice the script type! 21

Slide 22

Slide 22 text

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 =

Hello { doIt() }

22

Slide 23

Slide 23 text

Components 23

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Recommendation • Functional components • New projects • simplicity, hooks, and community direction. • Class components • Legacy projects 25

Slide 26

Slide 26 text

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!

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

Function Components 29

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

Example // function component function Link(properties) { return {properties.text}; } ReactDOM.render( , document.getElementById('root') ); properties = {"link": "http://www.tuni.fi", "text": "uta"} properties are read only, do not try to modify them 31

Slide 32

Slide 32 text

function Link(properties) { return {properties.text}; } function T3() { return (
); } ReactDOM.render( , document.getElementById('root') ); Typically React apps have one root element. 32

Slide 33

Slide 33 text

Using Arrays function App() { const a =

Hello World

; return
{[a,a,a,a]}
; } ReactDOM.render( , document.getElementById('root') ); 33

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

Arrow Functions and JSX const Link = (properties) => {properties.text} const T3 = () => (
) ReactDOM.render(, document.getElementById('root')) 35

Slide 36

Slide 36 text

Component Classes 36

Slide 37

Slide 37 text

React version >= v16.8 Function Component Class Component Properties X X State X (Hooks) X Lifecycle methods X (Hooks) X 37

Slide 38

Slide 38 text

Component Classes function Welcome(props) { return

Hello, {props.name}

; } ó class Welcome extends React.Component { render() { return

Hello, {this.props.name}

; } } Use this.props to access the properties 38

Slide 39

Slide 39 text

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 (
{time}
); } } ReactDOM.render( , document.getElementById('root') ); 39

Slide 40

Slide 40 text

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 (
{this.time}
); } } ReactDOM.render( , 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

Slide 41

Slide 41 text

41

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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 (
{this.time}
); } } ReactDOM.render( , 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

Slide 47

Slide 47 text

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 (
{this.time}
); } } ReactDOM.render( , 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!

Slide 48

Slide 48 text

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 (
{this.time}
); } } ReactDOM.render( , document.getElementById('root') ); 48 You can replace this syntax with class fields!

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

52 https://reactjs.org/docs/hooks-intro.html

Slide 53

Slide 53 text

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 (
{this.state.time}
); } } ReactDOM.render( , document.getElementById('root') ); The component has a state When changing the state... ... render is called 53

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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 (
{this.state.time} {this.state.place}
); } } When setting new state with "time" the "place" is still intact! This is merging 55

Slide 56

Slide 56 text

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 (
{this.state.time} {this.state.place}
); } } You can also use ESNext with class fields for state and omit the constructor 56

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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 (

name = {this.state.characterData.name}, height ={" "} {this.state.characterData.height}

); } } Public API for fetching Star Wars data Fetching data and changing state 58

Slide 59

Slide 59 text

Forms and Handling Events 59

Slide 60

Slide 60 text

Handling Events Example class Button extends React.Component { buttonClicked(event) { console.log('click') } render() { return (Click); } } Calling the buttonClicked Notice camelCase! event (SyntheticEvent) brings you information about the event 60

Slide 61

Slide 61 text

Using Event class Link extends React.Component { linkClicked(event) { event.preventDefault(); console.log('click') } render() { return (Link); } } Preventing the default behaviour link usually opens another page 61

Slide 62

Slide 62 text

Using this class Link extends React.Component { linkClicked(event) { event.preventDefault(); console.log(this) } render() { return (Link); } } It's NOT referring to object of Link – class! 62

Slide 63

Slide 63 text

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 (Link); } } Now this refers to Link object in the linkClicked - method 63

Slide 64

Slide 64 text

Solution: Binding in function call class Link extends React.Component { constructor(props) { super(props) } linkClicked(event) { event.preventDefault(); console.log(this) } render() { return (Link); } } Now this refers to Link object in the linkClicked - method 64

Slide 65

Slide 65 text

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 (Link); } } 65

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

Solution: Using Arrow Functions class Link extends React.Component { constructor(props) { super(props) } linkClicked = () => (event) { event.preventDefault(); console.log(this) } render() { return (Link); } } 67

Slide 68

Slide 68 text

Using State class Link extends React.Component { state = {stuff: ’’} linkClicked = (event) => { event.preventDefault(); this.setState({stuff: 'clicked'}) } render() { return (
Link

{this.state.stuff}

); } } Will update the view when state changes 68

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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 ( ); } } 71

Slide 72

Slide 72 text

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 ( ); } } 72 With async functions you can use await Using await

Slide 73

Slide 73 text

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 (
Display Alert

Given name = {this.state.name}

); } } 73

Slide 74

Slide 74 text

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 (

Given name = {this.state.name}

Given age = {this.state.age}

); } } 74

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

Optimizing handleChange handleChange = (event) => { // "name" or "age" let nameOfTheInput = event.target.name this.setState({[nameOfTheInput]: event.target.value}) } ES6: Computated property name 76

Slide 77

Slide 77 text

More About Lifecycle 77

Slide 78

Slide 78 text

render() • Most used lifecycle method and required • No side effects! • do not set state 78

Slide 79

Slide 79 text

componentDidMount() • componentDidMount is called after render and when component is ready • You can use setState • Good place to initiative API calls 79

Slide 80

Slide 80 text

componentDidUpdate() • If prop or state changes, componentDidUpdate is called • You can use setState, but beware of iternal loop! 80

Slide 81

Slide 81 text

Example of Lifecycle 81

Slide 82

Slide 82 text

Main App component const App = () => 82 This custom component fetches star wars character name with id 1 (Uses Swapi)

Slide 83

Slide 83 text

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

{this.state.name}

} } 83 1) render 2) componentDidMount 3) done (triggers render again)

Slide 84

Slide 84 text

Modification to App.js class App extends React.Component { state = {id: 1} change = (event) => { this.setState({id: event.target.value}) } render() { return
} } 84 Now user is asked the id. Fetching is done only once! Component is already mounted!

Slide 85

Slide 85 text

Modification to Character.js class Character extends React.Component { state = {name: ''} componentDidMount() { console.log('mount') } componentDidUpdate(prevProps, prevStat) { console.log('update') } render() { return

{this.state.name}

} } 85 This is called now several times! Only one time

Slide 86

Slide 86 text

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

{this.state.name}

} } 86 1) when component updates, fetching happens 2) fetching done, state changes, component updates again! 1) happens again!

Slide 87

Slide 87 text

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

{this.state.name}

} } 87 Trigger fetch only if props changed

Slide 88

Slide 88 text

React Hooks 88

Slide 89

Slide 89 text

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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

import React from "react"; export default function App() { const arr = React.useState("initial text"); const text = arr[0]; const setText = arr[1]; return (

{text}

setText("hello")}>Click me
); } 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

Slide 92

Slide 92 text

import { useState } from "react"; export default function App() { const [text, setText] = useState("initial text"); return (

{text}

setText("hello")}>Click me
); } 92 Object destruct Array destruct

Slide 93

Slide 93 text

import { useState } from "react"; export default function App() { const [number1, setNumber1] = useState(0); const [number2, setNumber2] = useState(0); return (

{number1 + number2}

setNumber1(Number(e.target.value))} placeholder="number 1" /> setNumber2(Number(e.target.value))} placeholder="number 2" />
); } 93 Multiple states

Slide 94

Slide 94 text

import { useState } from "react"; export default function App() { const [counter, setCounter] = useState(0); return (

{counter}

setCounter(counter + 1)}>+
); } 94 Button increases the count by one

Slide 95

Slide 95 text

function App() { let [counter, setCounter] = useState(0); const add = () => { setCounter(counter + 1); setCounter(counter + 1); }; return ( <>

Counter

{counter}

+ > ); } 95 Button increases the count still by one!

Slide 96

Slide 96 text

function App() { let [counter, setCounter] = useState(0); const add = () => { setCounter(counter + 1); setCounter(counter + 1); }; return ( <>

Counter

{counter}

+ > ); } 96 Passing function to a function…

Slide 97

Slide 97 text

function App() { let [counter, setCounter] = useState(0); const add = () => { setCounter(counter + 1); // 0 + 1 setCounter(counter + 1); // 0 + 1 }; return ( <>

Counter

{counter}

+ > ); } 97 Because of closure, we have 0 + 1 and 0 + 1!

Slide 98

Slide 98 text

function App() { let [counter, setCounter] = useState(0); const add = () => { setTimeout(() => { setCounter(counter + 1); }, 1000); }; return ( <>

Counter

{counter}

+ > ); } 98 Doing async stuff, clicking button several times. Triggers 0 + 1 several times, end result is 1

Slide 99

Slide 99 text

import { useState } from "react"; export default function App() { const [counter, setCounter] = useState(0); return (

{counter}

{ setCounter((prevCount) => prevCount + 1); setCounter((prevCount) => prevCount + 1); }} > +
); } 99 Passing a function and now it works!

Slide 100

Slide 100 text

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 ( <> +
    {people.map((person, index) => (
  • {person.name}
  • ))}
> ); }; 100

Slide 101

Slide 101 text

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 ( <>

Jokes

setAmount(Number(e.target.value))} type="number" placeholder="amount" /> Fetch {amount} jokes
    {jokes.map((joke, index) => (
  • {joke}
  • ))}
> ); } 101

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

{time}

} 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...

Slide 104

Slide 104 text

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

{time}

} 104 "dependencies", call effect function if depency value change. By giving empty array, it will be called only once

Slide 105

Slide 105 text

import { useState, useEffect } from "react"; export default function App() { const [resource, setResource] = useState("posts"); useEffect(() => { console.log("TRIGGER"); }, [resource]); return (

{resource}

setResource("posts")}>posts setResource("todos")}>todos
); } 105 Function is called when resource is changed

Slide 106

Slide 106 text

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 (

{resource}

setResource("posts")}>posts setResource("todos")}>todos
); } 106 Fetches stuff when resource changes!

Slide 107

Slide 107 text

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

{time}

} 107 The useEffect function returns a function...

Slide 108

Slide 108 text

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 (

{resource}

setResource("posts")}>posts setResource("todos")}>todos
); } 108 When trying to use async, it won't work because async function returns a promise by default

Slide 109

Slide 109 text

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 (

{resource}

setResource("posts")}>posts setResource("todos")}>todos
); } 109 Solution, create another function that is async

Slide 110

Slide 110 text

Custom Hook 110

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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
Loading...
; if (error) return
Error: {error.message}
; return (
{data &&
{JSON.stringify(data)}
}
); }; export default MyComponent; 112

Slide 113

Slide 113 text

Sidenote: Using Axios 113

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

{this.state.name}

} } 115 Notice the checking here that trigger the fetch only if props.id changed

Slide 116

Slide 116 text

HTTP POST with Axios axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); 116

Slide 117

Slide 117 text

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

{character.name}

} 117 Changing the state will trigger useEffect again! Eternal loop!

Slide 118

Slide 118 text

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

{character.name}

} 118 Every time a props.id is changed trigger the effect

Slide 119

Slide 119 text

Conditional Rendering 119

Slide 120

Slide 120 text

class Hello extends React.Component { render() { return

Hello!

} } class AccessDenied extends React.Component { render() { return

AccessDenied

} } class Login extends React.Component { render() { if(this.props.name === "Jack") return else return } } ReactDOM.render( , document.getElementById('root') ); Determining which component is returned 120

Slide 121

Slide 121 text

Lists and Keys 121

Slide 122

Slide 122 text

Lists class List extends React.Component { render() { let items = [
  • a
  • ,
  • b
  • ] return
      {items}
    } } 122

    Slide 123

    Slide 123 text

    Lists class List extends React.Component { render() { let items = ["a", "b"] let liItems = items.map((i) =>
  • {i}
  • ) return
      {liItems}
    } } 123

    Slide 124

    Slide 124 text

    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

    Slide 125

    Slide 125 text

    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) =>
  • {person.name}
  • ) return
      {liItems}
    } } 125

    Slide 126

    Slide 126 text

    Lifting State Up 126

    Slide 127

    Slide 127 text

    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

    Slide 128

    Slide 128 text

    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 Generate Random Name } } class DisplayRandomName extends React.Component { render() { return

    Display Random Name

    } } class Parent extends React.Component { render() { return
    } } 128 Generates random name This component wants to display the random name

    Slide 129

    Slide 129 text

    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 Generate Random Name } } class DisplayRandomName extends React.Component { render() { return

    Display Random Name

    } } class Parent extends React.Component { render() { return
    } } 129 When button clicked random name is in console. How to pass this to DisplayRandomName component?

    Slide 130

    Slide 130 text

    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 Generate Random Name } } class DisplayRandomName extends React.Component { render() { return

    Display Random Name

    } } class Parent extends React.Component { clicked = (name) => { console.log(name) } render() { return
    } } 130 .. And the function is passed here We have a function in the props now…

    Slide 131

    Slide 131 text

    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 Generate Random Name } } class DisplayRandomName extends React.Component { render() { return

    Display Random Name: {this.props.name}

    } } class Parent extends React.Component { state = {name: undefined} clicked = (name) => { this.setState({name}) // {name: 'tina'} } render() { return
    } } 131 Create state object from received name and send that to component

    Slide 132

    Slide 132 text

    Context API 132

    Slide 133

    Slide 133 text

    import React from "react"; const MyButton = () => Click; const Toolbar = () => (

    Title

    ); const App = () => { return (
    ); }; export default App; 133 When button presses, change button background to random color

    Slide 134

    Slide 134 text

    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 ( change()}> Click ); }; 134 color changes

    Slide 135

    Slide 135 text

    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 ( change()}> Click ); }; const Toolbar = () => { let [color, setColor] = useState("white"); const clicked = (myColor) => { setColor(myColor); }; return (

    {color}

    ); }; 135 Lifting state up, now title also changes

    Slide 136

    Slide 136 text

    function ThemedButton({ theme }) { return Click; } function Toolbar({ theme }) { return (
    ); } function App() { return ; } 136

    Slide 137

    Slide 137 text

    const ThemeContext = React.createContext(); function ThemedButton({ theme }) { const value = React.useContext(ThemeContext); return Click; } function Toolbar() { return (
    ); } function App() { return ( ); } 137

    Slide 138

    Slide 138 text

    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 ( change()}> Click ); }; const Toolbar = () => { const [color] = useContext(ColorContext); return (

    {color}

    ); }; const App = () => { let [color, setColor] = useState("white"); return ( ); }; 138

    Slide 139

    Slide 139 text

    const App = () => { let [color, setColor] = useState("white"); return ( ); }; => const App = () => { return ( ); }; 139 Let's hide this stuff to it's own file Let's create our own component for this

    Slide 140

    Slide 140 text

    import React from "react"; const ColorContext = React.createContext(); function ColorContextProvider(props) { let [color, setColor] = React.useState("hotpink"); return ( {props.children} ); } export { ColorContextProvider, ColorContext }; 140

    Slide 141

    Slide 141 text

    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 ( change()}> Click ); }; const Toolbar = () => { const [color] = useContext(ColorContext); return (

    {color}

    ); }; const App = () => { return ( ); }; export default App; 141

    Slide 142

    Slide 142 text

    Tooling 142

    Slide 143

    Slide 143 text

    Tooling • npx create-react-app myapp • npx create-next-app myapp • npx create-remix myapp • npx create-gatsby 143

    Slide 144

    Slide 144 text

    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

    Slide 145

    Slide 145 text

    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

    Slide 146

    Slide 146 text

    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

    Slide 147

    Slide 147 text

    index.html in react project You need to enable JavaScript to run this app.
    147

    Slide 148

    Slide 148 text

    In Browser Webpack will create this 148

    Slide 149

    Slide 149 text

    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( ); Inject where
    is Strict mode enables additional warnings 149

    Slide 150

    Slide 150 text

    React Router 150

    Slide 151

    Slide 151 text

    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

    Slide 152

    Slide 152 text

    index.js import React from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter } from 'react-router-dom' import App from './App'; ReactDOM.render(, document.getElementById('root')); 152

    Slide 153

    Slide 153 text

    App.js import React, { Component } from 'react'; import { Route, Link } from 'react-router-dom'; const Page1 = () => (

    Page 1

    ) const Page2 = () => (

    Page 2

    ) const Welcome = () => (

    Welcome

    ) class App extends Component { render() { return (

    Routing Example

    • Page 1
    • Page 2
    ); } } export default App; 153