React

🙋‍♂️

Frontend engineer

  • 9 years FE experience
  • AngularJS, Angular, Ember, React
  • currently Staff engineer at Productboard

❤️ 📸

❤️ 🛞

Slides + code

Schedule

  • 9-12 morning
    • recap of the previous day
    • breaks: 10, 11
  • lunch?
  • 13-17 afternoon
    • breaks: 14, 15, 16

Course

  • exercises build on top of each other!
  • discussions welcomed

Setup IDE

Install

Javascript

What is node.js?

  • environment for running Javascript
  • doesn't need browser
  • using V8

 ➡️ Hello world

  • create file index.js with Javascript code
  • execute file using: node index.js
console.log('Hello world');

Variables

  • var
  • let, const
let name = 'Martin';
name = 'Lucas';
const surname = 'Nuc';

Basic types

  • number
  • string
  • boolean
  • null, undefined
  • objects

Strings

// 1) double quotes
let name = "Martin";

// 2) single quotes
let name = 'Martin';

// 3) backticks
let name = `Martin`;

let fullName = `${name} Nuc`;

Strings

let name = 'Martin';
console.log(name.toUpperCase());

let characters = name.split('');
console.log(characters); // ['M', 'a', 'r'...]
  • have methods

null, undefined

  • undefined = value was not set
  • null = "nothing"
    • value set but it means "nothing" on purpose

Objects

later...

Loops

  • for
  • while

Conditions

let a = 5;
if (a === 5) {
    console.log('is five');
}
for (let i = 0; i < 10; i++) {
    // ...
}

let i = 0;
while(i < 10) {
    // ...
    i++;
}

Use === for comparison

Functions

  • sequence of commands
  • inputs
  • output
    • no return = undefined
function add(a, b) {
    return a + b;
}

How to create a function?

// 1) named function
function hello() {
    console.log('hello');
}

// 2) anonymous function
const hello = function() {
    console.log('hello');
}

// 3) anonymous using arrow function
const hello = () => console.log('hello');

named

anonymous

Arrow function

// with body
const one = () => {
    return 1;
};

// single returned statement
const two = () => 2;

Objects

Objects

  • not a primitive type
  • used for structured data
  • key: value
  • key = property
let teacher = {
    firstName: 'Martin',
    surname: 'Nuc',
    age: 32
}

Modify object

let teacher = {
    firstName: 'Martin',
    surname: 'Nuc',
    age: 32
}

// using dot notation
teacher.age = 33;

// using key name
teacher['age'] = 33;

let keyName = 'age';
teacher[keyName] = 33;

Shorthand when creating object

let firstName = 'Martin';
let age = 32;

// create object
let teacher = {
    firstName: firstName,
    age: age
}

// shorthand:
let teacher = {
    firstName,
    age
}

Spread operator

const martin = {
  firstName: 'Martin',
  age: 36
}

const richMartin = {
  ...martin,
  money: 1000000
} 
  • used to create a new object from another object

Stored as a reference

let teacher = {
    firstName: 'Martin',
    age: 32
}
let teacherTwo = teacher;

teacherTwo.age = 55;
console.log(teacher.age); // ????
  • not like primitive types
  • use Object.assign() to create a copy (not in IE11)

Methods in objects

const obj = {
    one: function() { return 1; },
    two: () => 2,
    three() {
        return 3;
    }
};
obj.four = () => 4;

obj.one();
obj.two();
obj.three();
obj.four();

Destructuring object

const obj = {
    firstName: 'Martin',
    surname: 'Nuc',
    age: 32
};

const { firstName, surname } = obj;
console.log(name, surname);

function printAge({age}) {
    console.log(age);
}

printAge({
    name: 'Martin',
    age: 32
});

Rest operator

const obj = {
    name: 'Martin',
    surname: 'Nuc',
    age: 32
};

const { name, surname, ...other } = obj;
console.log(other); // { age: 32 }

  • "rest" after destructuring

typeof

typeof 5 // 'number'
typeof 'hi' // 'string'
typeof {} // 'object'
typeof [] // 'object'
typeof undefined // 'undefined'

typeof null // 'object' !!

Arrays

Arrays

  • store multiple items
  • in order
  • .length = number of items
  • push to add
let items = [1, 2, 3]; // creation
items.push(4);

console.log(items); // [1, 2, 3, 4]
console.log(items.length) // 4

Access the index

  • index starts with 0
let arr = ['one', 'two', 'three'];

console.log(arr[0]); // 'one'
console.log(arr[1]); // 'two'

console.log(arr[5]); // undefined

Spread operator

  • expands array into individual items
  • used to create a new array from existing
const arr = ['one', 'two', 'three'];

const newArray = [...arr, 'four'];

➡️ Palindrome

  • create a function which determines whether input is palindrome
    • palindrome = word that reads the same backwards as forwards

Array methods

.join()

let arr = [1, 2, 3, 4];
let output = arr.join('-'); 
console.log(output); // '1-2-3-4'
  • creates string by joining items in the array

.reverse()

let arr = [1, 2, 3, 4];
arr.reverse();
console.log(arr); // [4, 3, 2, 1]

.forEach()

  • loops over the array
[1, 2, 3, 4].forEach(item =>
    console.log(item)
);

.every(), .some()

  • checks condition for every item
let result = [1, 2, 3, 4].every(item =>
    item > 0
);
console.log(result); // true

.map()

  • creates a new array
  • every item modified by a function
let result = [1, 2, 3, 4].map(item =>
    item + 1;
);
console.log(result); // [2, 3, 4, 5]

.filter()

  • creates a new array
  • includes only items that pass the condition
let result = [1, 2, 3, 4].filter(x => x > 2);
console.log(result); // [3,4]

.reduce()

  • accumulates intermediate result
[1,2,3,4].reduce((accumulator, current) =>
    accumulator + current
, 0); // 10
accumulator current result
0 1 1
1 2 3
3 3 6
6 4 10

➡️ Sum of positives

  • you have an array of numbers
  • (1) count how many positive numbers are there
    • example: [1,-4,7,12] => 3
  • (2) you would like to calculate sum of positive values only
    • example: [1,-4,7,12] => 1 + 7 + 12 = 20

String methods

.split()

  • creates an array
  • split by character
'Good morning'.split(' '); // ['Good', 'morning']

.replace()

  • replaces only first match and returns a new string
let result = 'Good morning'
    .replace('morning', 'afternoon');

Classes

What is a class?

  • template for future objects
  • needs to be instantiated using new keyword
  • like a "recipe" for a chocolate cake. Using recipe you make a cake (instance of a cake)
class Dog {
    bark() {
        console.log('woof-woof');
    }
}
let rex = new Dog();
rex.bark();

Might have properties

class Dog {
    setName(newName) {
        this.name = newName;
    }
    bark() {
        console.log('woof-woof, I am ' + this.name);
    }
}

let rex = new Dog();
rex.setName('Rex');
let lassie = new Dog();
lassie.setName('Lassie');

rex.bark();  // woof-woof, I am Rex
lassie.bark();  // woof-woof, I am Lassie
  • use this keyword to access properties

Constructor

class Dog {
    constructor(name) {
        this.name = name;
    }
    bark() {
        console.log('woof-woof, I am ' + this.name);
    }
}

let rex = new Dog('Rex');
rex.bark();  // woof-woof, I am Rex
  • method which is executed when the class is instantiated (when used with new)

Inheritance

class Animal {
    eat() {
        console.log('yum yum');
    }
}

class Dog extends Animal {
    constructor(name) {
        this.name = name;
    }
    bark() {
        console.log('woof-woof, I am ' + this.name);
    }
}

let rex = new Dog('Rex');
rex.bark();  // woof-woof, I am Rex
rex.eat(); // yum yum
  • class can extend another class
  • parent class should be more generic

setTimeout

setTimeout()

  • do something later
  • asynchronous
console.log(1);
setTimeout(() => console.log(2), 1000);
setTimeout(() => console.log(3), 2000);

Event loop

  • JS is single-threaded
  • things get pull from event loop
console.log(1);
setTimeout(() => console.log(2), 0);
console.log(3);

Context

this

What is context?

  • the value of this keyword
  • related not only to classes, it's everywhere
  • typically it's set to object which the function belongs to BUT you never know
class Dog {
    constructor(name) {
        this.name = name;
    }
    bark() {
        console.log('woof-woof, I am ' + this.name);
    }
}

let rex = new Dog('Rex');
rex.bark();

Context

  • depends on how the function is called
let obj = {
    name: 'Martin',
    sayHi() {
        console.log(this.name);
    }
}

obj.sayHi();
let fn = obj.sayHi;
fn();

Enforce context

  • using call+apply, bind
function fn(num) {
    console.log(this, num);
}

// classic invocation
fn(1); // this = undefined

// call + apply
fn.call('test', 2); // this = 'test'
fn.apply('test', [3]); // this = 'test'

// bind
let bound = fn.bind('test');
bound(4); // this = 'test'

Example

  • jQuery uses context:
$('a').each(function() {
  console.log(this.href);
});

Problem

  • What context does a callback function have?
let obj = {
  name: 'Martin',
  hi() {
    console.log(this.name);
    setTimeout(function () {
      console.log(this.name);
    }, 1000);
  }
}

obj.hi();

Arrow functions

  • always pass the current context
let obj = {
  name: 'Martin',
  hi() {
    console.log(this.name);
    setTimeout(() => {
      console.log(this.name);
    }, 1000);
  }
}

obj.hi();

 ➡️ Test your JS skills

  • Write a function to find the first not repeated (unique) character in a string
obj = {a: 2, b: 3};
for(key in obj) {
  console.log(key)
}

Promises

What are they for

  • to avoid "callback hell"
  • flat and readable code
  • mostly used for asynchronous operations
    • any asynchronous operation can be wrapped to a Promise

Problem: callback hell

setTimeout(() => {
  console.log('after 1s');
  setTimeout(() => {
    console.log('after another 1s');
    setTimeout(() => {
      console.log('after another 1s');
    }, 1000);
  }, 1000);
}, 1000);

Flat code with Promises

promise
  .then(fn1)
  .then(fn2)
  .then(fn3)

Promise state

  • pending
  • fulfilled (=resolved)
  • rejected

pending

rejected

fullfilled

.then(...)

.catch(...)

How to create a promise

= instantiate a Promise object

function wait5seconds() {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(), 5000);
    });
};

wait5seconds().then(() => console.log('after 5s'));

How to create a promise

shorthand for resolved / rejected promise

Promise.resolve()
Promise.reject()

Pass data

  • argument of resolve
  • obtained in the first parameter of then
function wait5seconds() {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve([1, 2, 3]), 5000);
    });
};

wait5seconds().then((data) => console.log(data));

1. Error handling - catch

function fetchData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => reject('This is error'), 5000);
    });
};

fetchData()
  .then((data) => console.log('success', data))
  .catch((error) => console.log(error));

Promise chaining

  • what you return in then you get in the next then
  • when it returns a Promise it will wait for that promise to resolve before going to next then
Promise.resolve('hey')
    .then(data => console.log(data)) // "hey"
    .then(() => anotherPromise()) // async
    .then(() => {
        return 'hello'; // sync
    })
    .then(param => console.log(param)); // "hello"

Waiting for multiple promises

  • Promise.all([promise1, promise 2])
    • returns a single promise - resolved when all of them are resolved
  • Promise.race([promise1, promise 2])
    • resolved when the fastest one is resolved 

➡️ Wrap setTimeout in Promise

  • create function wait
    • parameter: how long it should wait
  • use promise chain (.then) to count down:
    • console.log: 3,2,1, go!

async / await

async/await

  • syntactic sugar around promises
  • works only in async function
async function countDown() {
    console.log(3);
    await wait(1000);
    console.log(2);
    await wait(1000);
    console.log(1);
    await wait(1000);
    console.log('go!');
}

countDown().then(() => console.log('done'));

async/await

async function countDown() {
    console.log(3);
    await wait(1000);
    console.log(2);
    await wait(1000);
    console.log(1);
    await wait(1000);
    console.log('go!');
}

countDown().then(() =>
   console.log('done'));
function countDown() {
    console.log(3);
    return wait(1000)
      .then(() => console.log(2))
      .then(() => wait(1000))
      .then(() => console.log(1)
      .then(() => wait(1000))
      .then(() => console.log('go!'))
}

countDown().then(() =>
   console.log('done'));

Extract resolved data

async function fn() {
  const data = await Promise.resolve('hello');
}

Error handling in async fn

async function() {
    try {
        await Promise.reject('this is reason');
    } catch (err) {
        console.error(err);
    }
}

➡️ Rewrite using async/await

function fetchData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      Math.random() < 0.5 ? resolve(Math.random()) : reject('Fetching failed')
    }, 1000);
  })
}

function execute() {
  const results = [];
  fetchData('seznam.cz')
    .then((seznamData) => results.push(seznamData))
    .then(() => fetchData('google.com'))
    .then((googleData) => results.push(googleData))
    .catch(error => console.log(error))
    .then(() => console.log('done'))
}

fetchData ramdomly fails

NPM

 

Dependency management

Node package manager

{
  "name": "my-package",
  "version": "1.0.0",
  "description": "This is just description of my awesome package",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon --exec npm run start",
    "start": "tsc && node dist/index.js",
    "test": "mocha --opts mocha.opts"
  },
  "author": "Martin Nuc",
  "license": "ISC",
  "dependencies": {
    "@types/chai": "4.0.4",
    "@types/mocha": "2.2.43",
    "@types/node": "8.0.28",
    "@types/sinon": "2.3.4",
    "chai": "4.1.2",
    "mocha": "3.5.3",
    "nodemon": "1.12.1",
    "sinon": "3.2.1",
    "ts-node": "3.3.0",
    "typescript": "2.5.2"
  }
}

package.json

Scripts


"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
  "test": "react-scripts test",
  "eject": "react-scripts eject"
},
  • used to execute commands
  • npm dependencies executables resolution (from node_modules/.bin/*)

npm run <name>

Shortcut for start and test scripts only. For others you have to use npm run

Runs any script from npm.

npm start

npm test

👉

Dependencies

npm install lodash

installs lodash library:

Use library in your code

import lodash from 'lodash';
lodash.difference([1, 2, 3], [2, 3]);

Dependencies and GIT

  • we don't commit dependencies
  • put node_modules folder in .gitignore
  • npm install to install dependencies
{
  "name": "my-package",
  "version": "1.0.0",
  "description": "This is just description of my awesome package",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon --exec npm run start",
    "start": "tsc && node dist/index.js",
    "test": "mocha --opts mocha.opts"
  },
  "author": "Martin Nuc",
  "license": "ISC",
  "dependencies": {
    "@types/chai": "4.0.4",
    "@types/mocha": "2.2.43",
    "@types/node": "8.0.28",
    "@types/sinon": "2.3.4",
    "lodash": "4.17.5",
    "chai": "4.1.2",
    "mocha": "3.5.3",
    "nodemon": "1.12.1",
    "sinon": "3.2.1",
    "ts-node": "3.3.0",
    "typescript": "2.5.2"
  }
}

package.json

Semantic versioning

6.11.2

patch

minor version

major version

Semantic versioning

6.11.2

patch

minor version

major version

- major changes, breaks API

Semantic versioning

6.11.2

patch

minor version

- new features

- doesn't break API

major version

- major changes, breaks API

Semantic versioning

6.11.2

patch

- only bugfixes

minor version

- new features

- doesn't break API

major version

- major changes, breaks API

Let's talk about React! 💪

React

  • library for managing view
  • component based
    • helps split the app into small pieces
  • used to create SPA

Client

Server

Database

HTTP

browser

request

html page

Client

Server

Database

HTTP

React in browser, mobile app...

API

request

data

Single page application

Web server

html, js

Vite

Vite

  • tool for scaffolding react app
  • replaced create-react-app
npm create vite@latest

Project name: … (react-skoleni)
Select a framework: › React
Select a variant: › JavaScript

Important parts

package.json

  • describes the package
  • dependecies list
  • npm scripts

index.html

  • HTML
  • notice div id="root"

/public folder

  • contains assets

main.jsx

  • renders React into HTML element
ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

App.jsx

  • the main component

➡️ Start the React app

npm run dev

JSX

Elements

const label = React.createElement('a', {
  href: 'https://google.com'
}, 'Go to Google.com');
<a href="https://google.com">Go to Google.com</a>

children

props

type

What is JSX?

  • syntactic sugar around createElement
  • almost like HTML
  • transpiled to Javascript
  • example in App.tsx:
import React from 'react';

function App() {
  return (
    <div className="App">
      Hello
    </div>
  );
}
function App() {
  return React.createElement('div', { className: 'App' }, 'Hello');
}

Q: Why className?

import React from 'react';

function App() {
  return (
    <div className="App">
      Hello
    </div>
  );
}
function App() {
  return React.createElement('div', { className: 'App'}, 'Hello');
}

Q: What happens now?

import React from 'react';

function App() {
  return (
    <div>
      Yes
    </div>
    <div>
      No
    </div>
  );
}
function App() {
  return ????????
}

Solution: React Fragment

  • like empty element
  • when you want to return multiple elements - wrap them in fragment
import React from 'react';

function App() {
  return (
    <>
      <div>
        Yes
      </div>
      <div>
        No
      </div>
    </>
  );
}

➡️ Before we continue

  • remove everything in the body of App.jsx component
  • notice the browser reloads
function App() {
  return <h1>Hello</h1>;
}

Print a variable

function App() {
  let something = 'hello';
  
  return <div>{something}</div>;
}

Print an array

function Array() {
  let array = [1,2,3];
  
  return <div>
    {array.map((item, index) => <span key={index}>{item}</span>)}
  </div>;
}

Components

Component

  • reusable unit
  • just a function
  • input
    • ="props"
  • output
    • React element
function NameComponent(props) {
  return <h1>Hi, my name is {props.name}!</h1>;
}
function AnotherComponent() {
  return <NameComponent name="Martin" />
);

Component tree

  • split big problems to smaller ones

Component tree

Component

Component

Component

Component

Component

Component

Component tree

  • Stateful components (smart)
    • used to fetch data
    • data manipulation
  • Stateless components (dumb)
    • only display data
  • pass data down, emit events up

Component tree

Component

User info

ArticleList

Article

Today Weather

Article

I am smart 💡

Stateless component

  • everything to display is received via props
  • just a function
    • input: props (=properties)
    • output: React element
  • easy to test
function NameComponent(props) {
  return <h1>{props.name}</h1>;
}

How to use a component?

  • pass data down via props
function App() {
  return <NameComponent name="Martin" />
}

➡️ Dynamic table

  • create a component which displays a table using JSX
  • receives number of columns and rows as parameter
Hello Hello Hello Hello
Hello Hello Hello Hello
Hello Hello Hello Hello
<Table columns={4} rows={3} />

Event handling

  • React unifies API of events (link)
<button type="button" onClick={() => console.log('Hello')}>
  Hello world
</button>

State

useState

  • hook for storing data
  • instead of declaring variable
import React, { useState } from 'react';

function Counter() {
  const [name, setName] = useState('nobody');
	
  function handleGiveName(name) {
    setName(name);
  }

  return <div>
    My name is {name}.
    <button onClick={() => handleGiveName('Martin')}>
      Give me name
    </button>
  </div>
}

initial value

➡️ Create counter

  • create button with counter as text
  • start from 0
  • everytime you click the button the counter is increased

Component lifecycle

  • mounted
  • updated
    • triggered by change of state
    • triggered by change of props
    • ➡️ render
  • unmounted

Class components

  • rarely used nowadays
  • uses a class instead of a function
  • this.props
  • this.setState() to change state
  • life cycle hooks
    • componentDidMount
    • componentWillUnmount

Counter example

import React from 'react';

export class MyComponent extends React.Component {
  state = {
    counter: 0
  };

  increment() {
    this.setState({ counter: this.state.counter + 1 });
  }

  render() {
    const { counter } = this.state;
    return <div>
      Counter: {counter}
      <button type="button" onClick={() => this.increment()}>Increment</button>
    </div>
  }
}

➡️ Rewrite class component as a functional component

export class NumberGeneratorClass extends React.Component {

  constructor(props) {
    super(props);
    const generatedNumbers = [...Array(props.pregeneratedCount)].map(() => Math.random());
    this.state = {
      generatedNumbers
    };
  }

  generateNew() {
    this.setState({ generatedNumbers: [...this.state.generatedNumbers, Math.random()] });
  }

  render() {
    const { generatedNumbers } = this.state;
    return <div>
      {generatedNumbers.map((num, index) => <div key={index}>{num}</div>)}
    
      <button type="button" onClick={() => this.generateNew()}>Generate new</button>
    </div>
  }
}

Important things to notice

  • setter needs a new reference
  • the initial set is generated on every render

Conditions

  • use if statement
  • use ternary operator
function MyComponent() {
  const random = Math.random();
  
  if (random < 0.5) {
    return <span>lower</span>
  } else {
    return <span>higher</span>
  }
}
function MyComponent() {
  const random = Math.random();
  
  return <span>
    {random < 0.5 ? 'lower' : 'higher'}
  </span>
}
function MyComponent() {
  const condition = true;
  
  return <>{condition && <span>It's true</span></>
}

Styling app

Import CSS

  • global CSS
  • can use preprocessors (SCSS, SASS)
import './App.css';

function Component() {
  return <div className="red">Hello</div>
}
.red {
  color: red;
}

App.css

App.jsx

CSS modules

  • scoped CSS
  • can use preprocessors (SCSS, SASS)
  • css file must be named .module
import styles from './Component.module.css';

function Component() {
  return <div className={styles.red}>Hello</div>
}
.red {
  color: red;
}

App.module.css

App.jsx

Conditional styling without CSS modules

  • classnames library
  • npm i classnames @types/classnames
  • key = class to be applied
  • value = condition
import cn from 'classnames';

function ValidationState() {
  const [invalid, setInvalid] = useState(false);
  
  return <div className={cn({ red: invalid })}>
    Status
  </div>
}

Conditional styling with CSS modules

  • dynamic keys
import cn from 'classnames';
import styles from './ValidationState.module.css';

function ValidationState() {
  const [invalid, setInvalid] = useState(false);
  
  return <div className={cn({ [styles.red]: invalid })}>
    Status
  </div>
}

useEffect

useEffect

  • hook for side effects
    • = synchronization with external system
  • second argument say when it runs
    • empty - on every render
    • [ ] - only at the begining (=on mount)
    • [ variable ] - when a variable changes
  • should return cleanup function

useEffect example

  • tracks mouse position
export const MyMouse = () => {
  const [mousePosition, setMousePosition] = useState({x: 0, y: 0});
  
  useEffect(() => {
    const onMouseMove = event => {
      setMousePosition({
        x: event.clientX,
        y: event.clientY
      });
    };
    window.addEventListener('mousemove', onMouseMove);
    
    return () => {
      window.removeEventListener('mousemove', onMouseMove);
    };
  }, []);
  
  const {x, y} = mousePosition;
  return (
    <div>My mouse x position is {x} and y position is {y}</div>
  );
};

Try useEffect

  • show text with mouse position
  • show text with last mouse click coordinates
  • when the mouse position is
    • on the left to the last click - change text to green color
    • on the right to the last click - change text to red color

➡️ Create automatic counter

  • create a component which increases the counter every second
  • in the parent component create a button which shows/hides automatic counter component

Data down, events up

Creating own event

  • component emits event up
function ChildComponent(props) {
  return <button onClick={props.onTrigger}>emit event</button>;
}
<ChildComponent onTrigger={() => console.log('triggered')} />

parent component:

child component:

Children props

Children props

  • you might pass HTML as body of element:
<MyBytton onClick={...}>
  <Icon> Click me!
</MyBytton>
  • Button component receives react element via children prop:
function MyButton(props) {
  return (
    <button className="blue-button" onClick={props.onClick}>
      {props.children}
    </button>
  )
}

➡️ Create a dropdown

  • What is dropdown?
  • button which opens a menu when clicked
  • 3 components
    • smart + button + dropdown
  • Improvement: use children to pass dropdown content

www.google.com

www.instagram.com

www.facebook.com

Toggle button

emit click

DropdownComponent

shows

Controlled input

  • use component state as the only source of truth
function Component() {
  const [inputName, setInputName] = useState(name);

  return <>
    {inputName}
    
    <input
      value={inputName} 
      onChange={(e) => setInputName(e.target.value)} />
  </>
}

➡️ Create input

  • input of type number
  • how much the counter will increment

Strict mode

Strict mode

  • checks for common mistakes
  • only in dev mode
  • runs effects twice
  • renders components twice

API request

Axios library

Axios POST usage

import axios from 'axios';

const payload = { name: 'Martin' };
const response = await axios.post('/api/users', payload);
console.log(response);
  • good idea to place in useEffect - why?
import axios from 'axios';

const payload = { name: 'Martin' };
axios.post('/api/users', payload)
  .then(response => console.log(response));

➡️ Let's make http request 

  • open API request in browser to see structure of response
  • ​​display joke in the component
  • create a button to load another joke
GET https://api.chucknorris.io/jokes/random

Component tree

Component

User info

JokeFetcher

Joke

I am smart 💡

data down

Custom hooks

Custom hooks

  • separate logic from view
  • no render
  • named use*
  • hooks to component lifecycle
  • clear API

useMouseMove

const useMouseMove = () => {
  const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    const onMouseMove = (event) => {
      setMousePosition({
        x: event.clientX,
        y: event.clientY,
      });
    };
    window.addEventListener('mousemove', onMouseMove);
    return () => {
      window.removeEventListener('mousemove', onMouseMove);
    };
  }, []);

  return { x: mousePosition.x, y: mousePosition.y };
};
  • mouse position example
    • no input
    • outputs x, y of mouse

Fetch joke hook

  • encapsulate fetching joke logic into a custom hook
  • think about API first
  • Improvement: remember already fetched jokes, load next adds into the list

Debugging

Main tools

debugger;

Chrome dev tools

  • Network
  • Source
  • Performance
  • Application
  • React dev tools
    • Components
    • Profiler

Logging

  • Sentry.io
  • TrackJS

React Context

Context

  • "global" state for subtree of components
  • avoids passing props deep
  • Provider + Consumer
const MyContext = React.createContext(false);

function App() {
  return <MyContext.Provider value={true}>
    <Component />
  </MyContext.Provider>;
}

function Component() {
  const value = useContext(MyContext);
  
  return <div>{value}</div>;
}

Context

  • pass object with value + setter
const MyContext = React.createContext(0);

function App() {
  const [value, setValue) = useState(0);
  
  return <MyContext.Provider value={{ value, setValue}}>
    <Component />
  </MyContext.Provider>;
}

function Component() {
  const {value, setValue} = useContext(MyContext);
  
  return <>
    <div>{value}</div>
    <button onClick={() => setValue(value + 1)}>Click</button>
  </>;
}

➡️ Dark & Light theme 🌗

  • style any component to support dark & light theme
    • different background + text color
  • create a button to switch the theme

useRef

useRef

  • manipulate with DOM elements
  • object with mutable current property
function Component() {
  const inputRef = useRef(null);
  
  function handleClick() {
    inputRef.current.focus();
  }
  
  return <div>
    <input ref={inputRef} />
    <button onClick={handleClick}>Focus the input</button>
  </div>
}

Routing

React router

Building blocks

  1. Layout page
  2. App.tsx
  3. router

Routable pages

Navbar

Layout.jsx

import { Outlet } from "react-router";

export function Layout() {
  return <div>
    <NavBar />

    <Outlet />
  </div>
}
  • template for the root component

Here other routes render

Routable pages

<Outlet />

<Navbar />

App.jsx

import { RouterProvider, createBrowserRouter } from 'react-router-dom';

const router = createBrowserRouter([
  ... // (later)
]);

export function App() {
  return (
    <RouterProvider router={router} />
  );
}

Router

const router = createBrowserRouter([
  {
    path: '/',
    element: <Layout />,
    children: [
      {
        index: true,
        element: <Home />
      },
      {
        path: 'about',
        element: <About />
      },
      {
        path: 'articles',
        element: <ArticlesContainer />,
        children: [
          {
            path: ':articleId',
            element: <Article />
          }
          {
            path: ':articleId/comments',
            element: <ArticleComments />
          }
        ]
      }
    ]
  }
]);

Must have <Outlet /> inside

Layout component

Nested routes

  • components can define subroutes

Routable pages

Navbar

<Outlet>

Routable pages

Navigation using links

<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/articles">Articles</Link>
<Link to="/articles/{articleId}">Specific article</Link>

Imperative navigation

const navigate = useNavigate();

navigate('/articles');
  • using useNavigate() hook

Reading url parameters

import { useParams } from "react-router-dom";

function Joke() {
  const params = useParams();
  
  return (
    {params.category}
  );
}
/path-segment/:pathParam/something?query1=one&query2=two

useSearchParams

useParams

➡️ Create multiple pages

  • NavBar should be always visible
  • Create routes
    • / -> random joke
    • /categories -> categories list
    • /categories/:category -> joke from category
  • load joke based on category
GET https://api.chucknorris.io/jokes/random?category={category}

🎉

Made with Slides.com