Slide 1

Slide 1 text

Quick Start to ReactJS Jussi Pohjolainen 1

Slide 2

Slide 2 text

Intro 2

Slide 3

Slide 3 text

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

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 What..? ES is not easy language..

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

Learn fundamentals First! ECMAScript, Vanilla JS, HTTP Connection, Restful API .. 7

Slide 8

Slide 8 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 8

Slide 9

Slide 9 text

Tools 9

Slide 10

Slide 10 text

Tools • You can start learning ReactJS with just one html page, text editor and browser • But usually you will want to use some building tool • Create React App (CRA) – Depricated • Vite • Next.js • .. and others 10

Slide 11

Slide 11 text

create-react-app vs vite 11 Feature Vite + React Create React App (CRA) Bundler Vite (uses esbuild + Rollup) Webpack Dev Server Performance Extremely fast due to native ESM + esbuild Slower, especially with large projects Build Speed Very fast (optimized Rollup output) Slower (Webpack-based builds) Configuration Minimal by default, flexible via vite.config.js Limited flexibility without eject TypeScript Support Built-in, no extra setup needed Built-in JSX/TSX Support Built-in via esbuild Built-in Plugins Ecosystem Rich ecosystem, growing fast Mature, but less modern plugin architecture Legacy Browser Support Optional via plugin Supported by default Out-of-the-box Testing Not included, but works well with Vitest Comes with Jest setup Opinionated Setup Lightweight and minimal More opinionated (more boilerplate) Eject Option Not needed (config is always accessible) eject to modify config Community & Docs Growing, excellent documentation Mature, but CRA is declining in popularity SSR Support Supported via Vite SSR Not supported Status / Future Actively developed Deprecated for new projects, React team suggests alternatives

Slide 12

Slide 12 text

Tooling • CRA is depricated! • Other choices (recommendations by FB) • Next.js – SSR and React 19! • Remix • Gatsby • Vite 12

Slide 13

Slide 13 text

13 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 14

Slide 14 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. 14

Slide 15

Slide 15 text

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

{data.message}

:

Loading...

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

Slide 16 text

Next.js: SSR export async function getServerSideProps() { const res = await fetch('https:!//example.com/data.json'); const content = await res.json(); const props = { props: { data: content } } return props; } export default function Home({ data }) { return (

Data List!

    {data.map((item, index) !=> (
  • {item.name}!
  • ))} !
!
); } getServerSideProps i s a special Next.js function used to fetch data on the server side for each request to the page. App Router is disabled It runs on the server at request time and passes the fetched data as props to the React component. this is passed as a props to the component

Slide 17

Slide 17 text

React 18 (and Next.js): SSR export default async function Home() { const res = await fetch('https:!//example.com/data.json'); const data = await res.json(); return (

Data List!

    {data.map((item, index) !=> (
  • {item.name}!
  • ))} !
!
); } When component is async and it becomes ssr component Does not use browser- specific logic like useState, useE=ect

Slide 18

Slide 18 text

Key features of SSR • Data Fetching at Request Time • Every time a user requests the page, getServerSideProps runs on the server. • This ensures the page always displays the latest data. • SEO Benefits • The page is pre-rendered with the fetched data on the server. • Search engines can index the fully rendered HTML, improving SEO. • Secure Fetching • API calls or sensitive logic (like using API keys) are handled securely on the server.

Slide 19

Slide 19 text

Developer Control over SSR Optimization • SSR gives developers greater control over the application's infrastructure compared to CSR, which offloads rendering entirely to client devices. • CSR: The carbon footprint depends mainly entirely on the user's browser and device.

Slide 20

Slide 20 text

Real-World Considerations • Global Traffic: For international users, SSR can increase the carbon footprint if the server is centralized and far from the user. Using edge servers mitigates this. • High-Load Applications: SSR's carbon footprint increases with traffic, making caching and scaling critical for reducing environmental impact. • Device Impact in CSR: CSR shifts the rendering workload to client devices, which may be inefficient for lower-end or battery-powered devices, leading to higher energy consumption per user.

Slide 21

Slide 21 text

SSR vs CSR • SSR is generally better for reducing the carbon footprint in scenarios where: • The application serves static or semi-dynamic content. • Users rely on low-powered devices or poor internet connections. • Fast, efficient data delivery is critical. • CSR may be more efficient in scenarios where: • The application involves complex interactivity or frequent data updates. • The user base primarily uses high-performance devices. • Client-side caching can significantly reduce repeated server requests. • For sustainability, a hybrid approach (e.g., Static Site Generation or Server-Side Rendering with Hydration) often provides the best balance, leveraging the strengths of both SSR and CSR.

Slide 22

Slide 22 text

Hello World with React 22

Slide 23

Slide 23 text

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

Slide 24

Slide 24 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 • 24

Slide 25

Slide 25 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! 25

Slide 26

Slide 26 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! 26

Slide 27

Slide 27 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() }

27

Slide 28

Slide 28 text

Components 28

Slide 29

Slide 29 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. 29

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

React version >= v16.8 Function Component Class Component Properties X X State X (Hooks) X Life cycle methods X (Hooks) X 32 React Hooks enables same functionality

Slide 33

Slide 33 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 33

Slide 34

Slide 34 text

Function Components 34

Slide 35

Slide 35 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 35

Slide 36

Slide 36 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 36

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Using Arrays function App() { const a =

Hello World

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

Slide 39

Slide 39 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 39

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Component Classes 41

Slide 42

Slide 42 text

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

Slide 43

Slide 43 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 43

Slide 44

Slide 44 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') ); 44

Slide 45

Slide 45 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! 45

Slide 46

Slide 46 text

46

Slide 47

Slide 47 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... 47

Slide 48

Slide 48 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. 48

Slide 49

Slide 49 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! 49

Slide 50

Slide 50 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") 50

Slide 51

Slide 51 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 51

Slide 52

Slide 52 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. 52 You can replace this syntax with class fields!

Slide 53

Slide 53 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') ); 53 You can replace this syntax with class fields!

Slide 54

Slide 54 text

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

Slide 55

Slide 55 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) } } } 55

Slide 56

Slide 56 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) } } 56 Uses closures

Slide 57

Slide 57 text

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

Slide 58

Slide 58 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 58

Slide 59

Slide 59 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 59

Slide 60

Slide 60 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 60

Slide 61

Slide 61 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 61

Slide 62

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

Slide 63

Slide 63 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 63

Slide 64

Slide 64 text

Forms and Handling Events 64

Slide 65

Slide 65 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 65

Slide 66

Slide 66 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 66

Slide 67

Slide 67 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! 67

Slide 68

Slide 68 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 68

Slide 69

Slide 69 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 69

Slide 70

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

Slide 71

Slide 71 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 71

Slide 72

Slide 72 text

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

Slide 73

Slide 73 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 73

Slide 74

Slide 74 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/ 74

Slide 75

Slide 75 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(.....); 75

Slide 76

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

Slide 77

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

Slide 78

Slide 78 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}

); } } 78

Slide 79

Slide 79 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}

); } } 79

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

More About Lifecycle 82

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

Example of Lifecycle 86

Slide 87

Slide 87 text

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

Slide 88

Slide 88 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}

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

Slide 89

Slide 89 text

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

Slide 90

Slide 90 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}

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

Slide 91

Slide 91 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}

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

Slide 92

Slide 92 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}

} } 92 Trigger fetch only if props changed

Slide 93

Slide 93 text

React Hooks 93

Slide 94

Slide 94 text

React version >= v16.8 Function Component Class Component Properties X X State X (Hooks) X Life cycle methods X (Hooks) X 94 Let's now focus on hooks

Slide 95

Slide 95 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 95

Slide 96

Slide 96 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
); } 96 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 97

Slide 97 text

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

{text}

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

Slide 98

Slide 98 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" />
); } 98 Multiple states

Slide 99

Slide 99 text

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

{counter}

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

Slide 100

Slide 100 text

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

Counter

{counter}

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

Slide 101

Slide 101 text

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

Counter

{counter}

+ ); } 101 Passing function to a function…

Slide 102

Slide 102 text

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

Counter

{counter}

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

Slide 103

Slide 103 text

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

Counter

{counter}

+ ); } 103 Because we have twice the same update, react will do batching and only one update happens..

Slide 104

Slide 104 text

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

Counter

{counter}

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

Slide 105

Slide 105 text

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

{counter}

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

Slide 106

Slide 106 text

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

{counter}

{ setCounter((prevCount) => prevCount + 1); setCounter((prevCount) => prevCount + 1); }} > +
); } 106 First call: returns 0 + 1 => 1

Slide 107

Slide 107 text

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

{counter}

{ setCounter((prevCount) => prevCount + 1); setCounter((prevCount) => prevCount + 1); }} > +
); } 107 Second call: returns 1 + 1 => 2 We do not know when prev => prev + 1 is called React queues these. React processes these and calls functions in order. React internally tracks the current state value during this batch.

Slide 108

Slide 108 text

setCounter(() => 0); setCounter(() => 0); setCounter(() => 0); 108 Even though you called setCounter three times, React will only perform one actual state update, because all three result in the same value (0), and React will batch and coalesce them. React checks: “Is the new state different from the previous?” If nextState === currentState, then no update is applied, and no re-render occurs.

Slide 109

Slide 109 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}
  • ))}
); }; 109

Slide 110

Slide 110 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}
  • ))}
); } 110

Slide 111

Slide 111 text

Use Functional Approach • You perform multiple state updates in the same cycle • setCount(prev !=> prev + 1); setCount(prev !=> prev + 1); • Inside async callbacks, timeouts, promises, or effects • setTimeout(() !=> {setCount(prev !=> prev + 1); }, 1000) • If you're doing a single update, in a simple event handler, there's no risk using setCount(count + 1), altough the functional form guards you from subtle bugs in those edge cases. 111

Slide 112

Slide 112 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 112

Slide 113

Slide 113 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}

} 113 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 114

Slide 114 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}

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

Slide 115

Slide 115 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
); } 115 Function is called when resource is changed

Slide 116

Slide 116 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
); } 116 Fetches stuff when resource changes!

Slide 117

Slide 117 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}

} 117 The useEffect function returns a function...

Slide 118

Slide 118 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
); } 118 When trying to use async, it won't work because async function returns a promise by default

Slide 119

Slide 119 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
); } 119 Solution, create another function that is async

Slide 120

Slide 120 text

Custom Hook 120

Slide 121

Slide 121 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; 121

Slide 122

Slide 122 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; 122

Slide 123

Slide 123 text

Sidenote: Using Axios 123

Slide 124

Slide 124 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)); 124

Slide 125

Slide 125 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}

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

Slide 126

Slide 126 text

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

Slide 127

Slide 127 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}

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

Slide 128

Slide 128 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}

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

Slide 129

Slide 129 text

Conditional Rendering 129

Slide 130

Slide 130 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 130

Slide 131

Slide 131 text

Lists and Keys 131

Slide 132

Slide 132 text

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

    Slide 133

    Slide 133 text

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

    Slide 134

    Slide 134 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 134

    Slide 135

    Slide 135 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}
    } } 135

    Slide 136

    Slide 136 text

    Lifting State Up 136

    Slide 137

    Slide 137 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 137

    Slide 138

    Slide 138 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
    } } 138 Generates random name This component wants to display the random name

    Slide 139

    Slide 139 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
    } } 139 When button clicked random name is in console. How to pass this to DisplayRandomName component?

    Slide 140

    Slide 140 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
    } } 140 .. And the function is passed here We have a function in the props now…

    Slide 141

    Slide 141 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
    } } 141 Create state object from received name and send that to component

    Slide 142

    Slide 142 text

    Context API 142

    Slide 143

    Slide 143 text

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

    Title

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

    Slide 144

    Slide 144 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 ); }; 144 color changes

    Slide 145

    Slide 145 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}

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

    Slide 146

    Slide 146 text

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

    Slide 147

    Slide 147 text

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

    Slide 148

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

    Slide 149

    Slide 149 text

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

    Slide 150

    Slide 150 text

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

    Slide 151

    Slide 151 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; 151

    Slide 152

    Slide 152 text

    Tooling 152

    Slide 153

    Slide 153 text

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

    Slide 154

    Slide 154 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. 154

    Slide 155

    Slide 155 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. 155

    Slide 156

    Slide 156 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 156

    Slide 157

    Slide 157 text

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

    Slide 158

    Slide 158 text

    In Browser Webpack will create this 158

    Slide 159

    Slide 159 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 159

    Slide 160

    Slide 160 text

    React Router 160

    Slide 161

    Slide 161 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 161

    Slide 162

    Slide 162 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')); 162

    Slide 163

    Slide 163 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; 163