# Antra SEP ###### tags: `notes` ### nvm vs npm nvm (Node Version Manager) is a tool that allows you to download and install Node.js. npm (Node Package Manager) is a tool that allows you to install javascript packages. --- ## HTTP ### local storage vs cookie cookie can be send to backend, localstorage only seen by user difference: structure(key value pairs/string), cookie auto send data to backend ## HTML ### lang=en in html ![](https://i.imgur.com/tcRQ0Rs.png) what's the difference btw Translation, Localization, and Internationalization? * Internationalization(i18n): design for different locales * Localization(L10N): translate into specific language The name in rules ``` const foo = input[0] + (input.length-2) + input[input.length-1] ``` How does this work? ### href vs src src: for source location href: hyper link ### Why we put css link in head? If we put it inside body and we have elements inside body, they might not be painted => also has performance issue if we put inside body ### node vs element If we try to get element from body using script in head, will it work? ``` getElementByTagName('h1') //yes querySelectorAll('h1') //no, nodeList is not dynamic ``` dom: text, element getElementById: return Element querySelectorAll: return NodeList getElementByTagName: return a live html collection(element) => dynamic, will changed after some changes in DOM tree ### async & defer * if use `async` in script, it'll wait for body scripts * `async` attribute is only for external scripts (and should only be used if the src attribute is present) ### meta The way to talk with the browser, is helpful for SEO, before rendering ![](https://i.imgur.com/8xyKnSt.png) charset: how we encode our document viewport: initial width 1 pixel is not 1 pixel: depends on different device, different resolution KPI ### Traverse nodes ![](https://i.imgur.com/ebxK9KD.png) Build a function inside our web api ``` for (let i = 0; i < node.childNodes.length; i++) { get(node.childNodes[i]) } ``` ### DOM (Document Object Model) JS object that allow us to change the HTML. It's the `window.document` object on browser. ### semantic http://web-accessibility.carnegiemuseums.org/resources/ #### provide non-semantic tag with semantic meating using aria attribute ### SVG vs Canvas Both are tag to draw something. * SVG(Scalable Vector Graphics): help create scalable graphic which will not lose resolution even if it's bigger. SVG icon ex. fontawesome * Canvas: main purpose is just to draw. We can use Canvas to draw the layout and inside the layout, use SVG graphics ## CSS ### CSS inline, block, inline-block inline-block: can set height & width inline: only takes the least space that element needs ### class naming rule should follow rules to name the class https://seesparkbox.com/foundry/bem_by_example ### css games ### css reset v.s. css normalize * css reset 10 years ago, we have many browsers, each of them provide different default style, we have to reset the style so that it'll follow our rule. Now, most of the browsers follow same standard styles, but not all of them, so we still need to reset css. When we see `user agent stylesheet`, that means this style is provided by our browser. ![](https://i.imgur.com/Ug7kEOH.png) ``` *::before, *::after, * { margin: 0; padding: 0; box-sizing: border-box; } ``` The `*` includes everything except psuedo elements. * css normalize A library that helps us use the standard way to reset css. https://meyerweb.com/eric/tools/css/reset/reset.css ### box-sizing Give the ability to control the width and height. `box-sizing: border-box` will include the border&padding into width calculating. * content-box: width = content This box will looks like 150px width because `content-box` make the width do not include the border. ``` .box { width: 100px; height: 100px background-color: black; border: 50px solid black; box-sizing: content-box //default } ``` * border-box: width = content+padding+border This box will looks like 100px width. ``` .box { width: 100px; height: 100px background-color: black; border: 50px solid black; box-sizing: border-box } ``` Developers like boxer-box because we don't have to calculate tehe real width that'll appear on the screen. And it's why we want to reset css as `border-box`. ### Responsive Design In css we use media query, in html use meta tag. ### mobile first designing for mobile before designing for desktop --- ## JS **topic: data type, var/let/const, class, IIFE** ### Primitive Data * **Primitives**: string, number, boolean, undefined, symbol, null A primitive is not an object(except null) and has no methods of its own. All primitives are *immutable*. * **structural root primitive**: null every object comes from null, null is primitive data because in the source code of ```typeof```, they didn't check for the null value ``` typeof(null) //object ``` ![](https://i.imgur.com/Ee8D6vq.png) ### type coersion automatic or implicit conversion of values from one data type to another (such as strings to numbers) Ex. coerced the 9 from a number into a string ``` const value1 = '5' const value2 = 9 let sum = value1 + value2 console.log(sum) //59 ``` Ex. coerced the '911','abc' from a string into a number ``` typeof(+'911') //number +'abc' //NaN typeof(+'abc') //number ``` Ex. coerce boolean into number ``` console.log(true+false) //1 console.log(+true) //1 console.log(+false) //0 typeof(+true) //number ``` ### == vs. === ``` '911' == 911 //true '911' === 911 //false +'911' === 911 //true ``` ### Object data * Structural Types: object ex. function, Function, Boolean, Symbol, Error, Number Here are some of the standard objects. Notice some of these were on the primitive list too, but don’t confuse them. These act as **constructors** to create those types. ``` var a = Boolean('a') console.log(a) //true typeof(a) //boolean ``` * Methods of creating object ``` var obj = {} //or var obj2 = Object.create({}) ``` * object cannot be compared ``` obj == obj2 //false ``` * object will hold the address If we change data in an object, it'll just be changed ``` var obj = { name: 'David' } function foo(input) { input.name = 'Jojo' console.log(input) // {name: 'Jojo'} } foo(obj) console.log(obj) // {name: 'Jojo'} ``` --- ### function keyword and var keyword ``` console.log(foo, foo2) //function, undefined function foo() {} var foo2 = function() {} //var foo2 will hoisting with undefined value, but not function ``` ### var, let, const scope: function, block * Assign value to un-declared variable ``` a = 3 console.log(a) //3 ``` * Get value before assigning value var will have hoisting ``` console.log(a) //undefined var a = 3 console.log(a) //3 ``` * Get value before assigning/declaring value ``` console.log(a) //reference error a = 3 console.log(a) //3 ``` ``` var a = 5 function foo(input) { input = 6 console.log(input) //6 } foo(a) console.log(a) //5 ``` **const** cannot be re-assigned, but object properties can, because the address of object is not changed * const for object ``` const obj = { name: 'David' } const obj2 = { name: 'Coco' } obj = obj2 //error obj.name = obj2.name //fine ``` * const for array ``` const arr = [1, 2, 3] arr[0] = 100 console.log(arr) //[100, 2, 3] ``` We'll use const in declaring object, array, and some primitive data that'll not be changed * function can hoisting, too! ``` console.log(foo()) //Dio function foo() { return 'Dio' } ``` * But, if we use const and arrow function to declare a function, it'll not be hoisted ``` console.log(foo()) //Ref error const foo = () => 'Dio' ``` * function will be overwrite ``` function foo() { return 'Jojo' } console.log(foo()) //Dio!!! function foo() { return 'Dio' } ``` | feature | var | let | const | function | | -------- | -------- | -------- | -------- | -------- | | hoisting | yes(fn scope) | no(block scope) | no(b) | yes(fn) | | re-assign| yes | yes | no | yes | --- #### Undefined v.s not defined(Reference Error) Undefined: variable is declared but not defined not defined: variable is not even declared --- ### Scope Scope determines the accessibility (visibility) of variables. ```if, for, while``` are all block statements * var will not be influenced by block scope ``` function foo() { console.log(a) //undefined if (1) { //from here var a = 6 } //to here, is a block scope console.log(a) //6 } foo() ``` * let will be influenced by block scope ``` function foo() { console.log(a) //not defined, ref error if (1) { let a = 6 } console.log(a) //not defined, ref error } foo() ``` * ``` var a = 3 function foo() { if (1) { var a = 6 } console.log(a) //6 } foo() console.log(a) //3 ``` * If cannot find variable, it'll find from the parent scope ``` var a = 3 function foo() { if (1) { let a = 6 } console.log(a) //3 } foo() ``` * ``` var a = 3 function foo() { console.log(a) //3 if (1) { a = 6 } console.log(a) //6 } foo() ``` --- ### Class (start from 47:35) #### Create using syntax sugar in ES6 (js has no class) ``` class Person { constructor(name) { this.name = name this.age = age } } var david = new Person('David', 12) console.log(david) ``` * getter/setter get: ```class.getterMethod```, get class variable set: ```class.setterMethod = newValue```, set new value to the class variable ``` class Person { constructor(name, age) { this._name = name this._age = age } get name() { return this._name } set name(newName) { this._name = newName } } const david = new Person('David', 12) console.log(david) >> { _age: 12, _name: "David" } //getter console.log(david.name) >> 'David' //setter david.name = 'Coco' console.log(david) >> { _age: 12, _name: "Coco" } ``` * Add new methods to class ``` class Person { constructor(name, age) { this._name = name this._age = age } get name() { return this._name } set name(newName) { this._name = newName } walk() { console.log(`${this._name} is walking.`) } } const david = new Person('David', 12) david.walk() >> David is walking. ``` --- #### Create using function ``` function Person(name, age) { this.name = name this.age = age } const david = new Person('David', 12) console.log(david) ``` * Add new methods to class 1. in function (can be called by child) 2. use ```prototype``` on class (cannot be called by child) 3. use ```__proto__``` on any instance (accessible for all instances) 4. use ```class.method``` (only for the single instance) ``` function Person(name, age) { this.name = name this.age = age //method 1: thid.walk = function() { console.log(`${this.name} is walking.`) } } //method 2: Person.prototype.walk = function () { console.log(`${this.name} is walking.`) } ``` #### ```__proto__``` v.s. ```prototype``` ```__proto__``` is the property of **instance** object ```prototype``` is for the object itself ``` function Person(name, age) { this.name = name this.age = age } const p = new Person('David', 12) console.log(p.__proto__ === Person.prototype) //true ``` We can also add method use ```__proto__```: ``` //method 3: p.__proto__.walk = function() { console.log(`${this.name} is walking.`) } p.walk() >>> David is walking. ``` *Note this method will directly be added to the class!!!* ``` const p2 = new Person('Jojo', 12) p2.walk() >>> Jojo is walking. ``` If we just want to add a method on a single instance, we can simply do this: ``` //method 4: p.walk = function() { console.log(`${this.name} is walking.`) } ``` --- #### OOP There's no OOP concept in JS, but to mimic the OOP as in other languages, the class sugar syntax is introduced. * **Encapsulation** (private data is protected) Before ES2019, though we have getter/setter, people can still get/set data in class without getter/setter. Developers often using the underscore convention (```_data```) to indicate a private data. In ES2019, private class fields are defined using a hash ```#``` prefix 1. private data must be declared ourside of constructor 2. can set default value to it 3. or use constructor to give it value 4. or simply use setter to set value ``` class Person { //private data must be declared ourside of constructor! #name #age = 0 //this is not necessary constructor(name, age) { this.#name = name this.#age = age } get name() { return this.#name } set name(newName) { this.#name = newName } get age() { return this.#age } set age(newAge) { this.#age = newAge } walk() { console.log(`${this.#name} is walking.`) } } const p = new Person('Coco', 22) //can't get private data directly console.log(p.#age) >>> error //use getter p.age >>> 22 //use setter p.name = 'David' ``` https://www.sitepoint.com/javascript-private-class-fields/ * **Inheritance** --- class way --- 1. ```super```: used to access and call functions on an object's parent 2. child class can access parent's methods ``` class Employee extends Person { constructor(name, age, company) { super(name, age) this.company = company } } const david = new Employee('David', 12, 'Antra') david.walk() >> David is walking. ``` --- function way --- This will not inherit methods from parent. ``` function Employee(name, age, company) { Person.call(this, name, age) this.company = company } ``` Inherite methods from parent: ``` Employee.prototype = Object.create(Person.prototype) ``` https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance * **polymorphism** In general OOP: it'll include overloading & overriding **overloading**: same method name, passing different number of parameters can use different methods **overriding**: replace parent method with same method name https://medium.com/@totoroLiu/%E7%89%A9%E4%BB%B6%E5%B0%8E%E5%90%91-object-oriented-programming-%E6%A6%82%E5%BF%B5-5f205d437fd6 But in JS, there's no overloading, only overriding ``` //parent class Person { constructor(name, age) { this._name = name this._age = age } get name() { return this._name } set name(newName) { this._name = newName } walk() { console.log(`${this._name} is walking.`) } } //child class Employee extends Person { constructor(name, age, company) { super(name, age) this.company = company } walk(num) { console.log(`${this.name} walk for ${num} times`) } } const david = new Employee('David', 12, 'Antra') //parent method is overrided david.walk() >>> David walk for undefined times ``` Even if we define a new one in child, it won't be solved, because JS will use the last decalred function, if they're using the same name. ``` class Employee extends Person { constructor(name, age, company) { super(name, age) this.company = company } walk() { console.log(`${this._name} is walking.`) } walk(num) { console.log(`${this.name} walk for ${num} times`) } } const david = new Employee('David', 12, 'Antra') david.walk() >>> David walk for undefined times ``` * **abstract** --- ### IIFE Why do need IIFE? Create a function scope, so that it'll not influence the global object. Everything declared inside the IIFE will not effect global scope. --- ### modular pattern --- ### Loop through array * for loop - for ``` const arr = [1, 2, 3] for (let i = 0; i < arr.length; i++) { console.log(arr[i]) } ``` * for loop - for of ``` const arr = [1, 2, 3] for (let num of arr) { console.log(num) } ``` ### forEach v.s map * **forEach** will pass a callback function, execute the function once for each element of the array. Therefore, it cannot do return/break syntax: ```arr.forEach( (currVal, idx, currArr) => {} )``` ``` const arr1 = [1,2,3] console.log(arr1.forEach(e => { if (e === 2) { console.log('return here') return e } })) //undefined arr1.forEach(e => { if (e === 2) { break //not work } }) ``` * **map** will create a new array according to return value, it cannot stop the loop, too ``` const arr = [1,2,3] console.log( arr.map(function (ele, index, currArr) { console.log(ele, index, currArr) if (ele !== 2) { return 2 } }) ) //[2, undefined, 2] ``` * map cannot directly change value with the current element parameter, we should change on ```currArr``` instead. ``` const arr = [1,2,3] arr.map(function (ele, index, currArr) { console.log(ele, index, currArr) if (ele === 2) { ele = 12 } }) console.log(arr) //[1,2,3] arr.map(function (ele, index, currArr) { console.log(ele, index, currArr) if (ele === 2) { currArr[index] = 12 } }) console.log(arr) //[1,12,3] ``` * **Build the forEach function** syntax: ```myForEach( (currVal, idx, currArr) => {})``` return: none ``` Array.prototype.myForEach = function(callback) { for (let i = 0; i < this.length; i++) { callback(this[i], i, this) } } ``` Test: ``` const arr = [1, 2, 3] const cb = function(currVal, idx, currArr) { if (currVal === 2) { currArr[idx] = 12 } } arr.myForEach(cb) console.log(arr) //[1, 12, 3] ``` > [Answer the question] > Q: Why forEach cannot return? > A: Because there's no designed return * **Build the map function** syntax: ```myMap( (currVal, idx, currArr) => {})``` return: a new array after executing callback fn ``` Array.prototype.myForEach = function(callback) { let newArr = [] for (let i = 0; i < this.length; i++) { newArr.push(callback(this[i], i, this)) } return newArr } ``` Test: ``` const arr = [1, 2, 3] const cb = function(currVal, idx, currArr) { if (currVal === 2) { return 2 } } arr.myMap(cb) console.log(arr) //[undefined, 2, undefined] ``` --- ### filter, find * **filter** Creates a new array with all elements that pass the test implemented by the provided function. sytax: ```arr.filter( callback(currVal, idx, currArr), thisArg ) ``` thisArg(optional): value to use as **this** when executing callback Ex. ``` function isInRange(value) { if (typeof value !== 'number') { return false; } return value >= this.lower && value <= this.upper; //here the "this" refers to the thisArg instance } let data = [10, 20, "30", 1, 5, 'JavaScript filter', undefined, 'example']; let range = { lower: 1, upper: 10 }; let numberInRange = data.filter(isInRange, range); console.log(numberInRange); // [10, 1, 5] ``` * **Build my filter** ``` Array.prototype.myFilter = function(callback) { let newArr = [] for (let i = 0; i < this.length; i++) { if ( callback(this[i], i, this) ) { newArr.push(this[i]) } } return newArr } ``` Test: ``` const arr = [1, 2, 3] const cb = e => e===2 arr.myFilter(cb) console.log(arr) //[2] ``` * **find** Return the first elements that pass the test implemented by the provided function. sytax: ```arr.find( callback(currVal, idx, currArr), thisArg ) ``` thisArg(optional): value to use as **this** when executing callback * **Build my find** ``` Array.prototype.myFind = function(callback) { for (let i = 0; i < this.length; i++) { if ( callback(this[i], i, this) ) { return this[i] } } } ``` Test: ``` const arr = [1, 2, 3, 2] const cb = e => e===2 arr.myFind(cb) console.log(arr) //2 ``` --- ### some, every --- ### reduce Executes a reducer function on each element of the array, resulting in single output value sytax: ```arr.reduce( callback(acc, curr, idx, currArr ),initialAcc)``` initialAcc: initial value for accumulator * **Build my reduce** ``` Array.prototype.myReduce = function(callback, initialAcc) { let acc = initialAcc || 0 for (let i = 0; i < this.length; i++) { acc = callback(acc, this[i], i, this) } return acc } ``` Test: ``` const arr = [1, 2, 3] const cb = (acc, curr) => acc+curr console.log(arr.myReduce(cb)) //6 ``` --- ### Object forEach ``` const obj = { name: 'Dio', age: 200 } for (let key in obj) { console.log(obj[key]) } //'Dio' //200 ``` --- ### ES6 spread operator, rest parameters * rest parameter The rest parameter syntax allows a function to accept an indefinite number of arguments as an array. Ex. ``` function foo(num, num2, ...args) { console.log(args) } foo(1, 2, 3, 4) //[3,4] ``` * spread operator Allows an iterable such as an array expression or string to be expanded. Ex. ``` const foo = (input) => { const set = new Set([...input]) return [...set].join('') } console.log(foo('qwerqwe')) //qwer ``` --- ### shallow copy, deep copy * **Shallow Copy**: addresses of X, Y is the same * **Deep Copy**: copy of all the members of X, allocates different memory location for Y There're 3 deep copy methods: 1. **spread operator** ``` const obj = {name: 'Dio'} const obj2 = { ...obj } ``` 2. **JSON** ``` const obj = {name: 'Dio'} const obj2 = JSON.parse(JSON.stringify(obj)) ``` 3. Object.assign syntax: ```Object.assign(target, ...sources)``` ``` const obj = {name: 'Dio'} const obj2 = Object.assign({}, obj) ``` --- ### object? array? The ```typeof``` array and object are both object, how to differentiate an array? * array ``` let arr = [] // if there's Array keyword in object's constructor, it's array arr.__proto__.constructor.toString().indexOf('Array') > -1 ``` --- ### Destructure(ES6) * destructure array ``` const [a,b] = [1, 2] console.log(a, b) //1,2 const [a, b] = [ {name: 'David'} , 2] console.log(a) //{name: 'David'} ``` * destructure object Note: getting value with wrong key name will cause error ``` const obj = {name: 'David', age: 20} //wrong const [name, age] = obj //wrong const {a, b} = obj //correct const {name, age} = obj console.log(name, age) //David, 20 ``` * rename object key ``` const {name: a, age} = obj console.log(name, age) //result, 20 console.log(a, age) //David, 20 ``` * React Hook ``` const arr = [ { name: 'Dio' }, data => { arr[0] = { ...arr[0], ...data } }, ] const [state, setState] = arr setState({ age: 200 }) console.log(arr[0]) //{ age: 200, name: 'Dio' } console.log(state) //{ name: 'Dio' } ``` state data does not change because it's destructured before calling setState --- ### this We have no idea what is `this` keyword until we know which part invoke this object. Refers to the object that owns ```this``` * global browser: Window[global] IDE: Object [global] * Object owns the method ``` const obj = { foo() { console.log(this) } } obj.foo() //obj ``` * Newly created instance ``` class Person { constructor(name) { this.name = name } bar() { console.log(this) } } const p = new Person('Dio') p.bar() //the object p console.log(p) //the object p ``` --- ### call, apply, bind * **bind** Create a new function and bind the method to an object ``` const obj = { pi: 3.14, getPi: function() { return this.pi } } function foo() { console.log(`The value of pi is ${this.getPi()}`) } foo() //error: this.getPi is not a function const bar = foo.bind(obj) bar() //The value of pi is 3.14 ``` * We can use parameters of the newly binded function ``` const obj = { pi: 3.14, getPi: function() { return this.pi } } function foo(circle, radius) { console.log(`Area of circle ${circle} is ${this.getPi() * radius ** 2}`) } const bar = foo.bind(obj) bar('Dio', 2) //"Area of circle Dio is 12.56" ``` * **call** Bind the method to the first parameter(the object) syntax: ```fn.call(obj, par1, par2, ...)``` ``` foo.call(obj, 'Dio', 2) //"Area of circle Dio is 12.56" ``` * **apply** syntax: ```fn.apply(obj, [par1, par2, ...])``` ``` foo.apply(obj, ['Dio', 2]) //"Area of circle Dio is 12.56" ``` --- ### Arrow function(ES6) ```=>``` will automatically bind ```this``` to the defining object. It’s no longer necessary to add bind declarations. 🎉 * A normal function inside a method without binding ```this``` refers to global object ``` const myObj = { myMethod(item) { console.log(this) //myObj const foo = function() { console.log(this) //global object } foo() } } ``` * With bind ```this```, it can refer to the object that owns this function ``` const myObj = { myMethod(item) { console.log(this) //myObj const foo = function() { console.log(this) //myObj }.bind(this) foo() } } ``` * Or, we can use arrow function to automatically bind ```this``` ``` const myObj = { myMethod(item) { console.log(this) //myObj const foo = () => { console.log(this) //myObj } foo() } } ``` * arrow function cannot be used to create constructor function Normal way to create a constructor: use function ``` const Car = function(color) { this.color = color } const car = new Car('red') console.log(car) //{color: 'red'} ``` Wrong way: use arrow function ``` const Car = (color) => { this.color = color } const car = new Car('red') console.log(car) //Car is not a constructor ``` * arguments of arrow function and normal function ``` function foo() { console.log(arguments) //{0: "a", 1: "b"} const bar = function() { console.log(arguments) //{0: "c", 1: "d"} } bar('c', 'd') } foo('a', 'b') ``` ``` function foo() { console.log(arguments) //{0: "a", 1: "b"} const bar = () => { console.log(arguments) //{0: "a", 1: "b"} } bar('c', 'd') } foo('a', 'b') ``` --- ### Closure A closure gives you access to an outer function’s scope from an inner function ``` const foo = function() { let a = 5 return function() { console.log(a) } } const bar = foo() bar() ``` * After running the method, JS will not destroy variable inside it, so it'll not be reset ``` const limitedFunction = function(num, cb) { let counter = 0 return function(...args) { if (counter <= num) { cb(...args) counter++ } else { console.log('over limit') } } } const fn = limitedFunction(3, (a, b) => console.log(a + b)) fn(2, 3) //5 fn(2, 3) //5 fn(2, 3) //5 fn(2, 3) //over limit ``` * code challenge Q: create a function foo ``` console.log(foo(4, 23, 2)(2)(12,1)) ``` A: ``` const foo = (...args1) => (...args2) => (...args3) => { return [...args1,...args2, ...args3].reduce( (acc, curr) => acc+curr) } ``` --- ### Async, Event Loop Mechenism for JS to handle the async task. **contains 3 parts**: call stack, async API, message queue(task) async statement: ```setTimeout```, ```ajax```, ```promise```, ```click``` event **Main Stack**: where all javascript code gets pushed and executed one by one **Callback Queue**: where asynchronous code gets pushed to, and waits for the execution. **Job Queue**: Apart from Callback Queue, reserved only for ```new Promise()``` functionality. Has higher priority than callback queue. **Event Loop**: keeps running 1. checks Main Stack, and run frames inside it until empty 2. checks Callback Queue, pops messages from it to the Main Stack for the execution Ex. * step 1: push to call stack ``` for (var i = 0; i < 5; i++) { setTimeout(() => console.log(i), i*1000 ) } //call stack: foo, i //async API: console.log(i), console.log(i), console.log(i), console.log(i), console.log(i) //message queue: ``` * step 2: push to async api ``` for (var i = 0; i < 5; i++) { setTimeout(() => console.log(i), i*1000 ) } //call stack: foo, i //async API: console.log(i), console.log(i), console.log(i), console.log(i), console.log(i) //message queue: ``` * step 3: push from async api to message queue ``` for (var i = 0; i < 5; i++) { setTimeout(() => console.log(i), i*1000 ) } //call stack: foo, i //async API: //message queue: console.log(i), console.log(i), console.log(i), console.log(i), console.log(i) ``` * result ``` 5 5 5 5 5 ``` * Modify 1 Create a new scope and put the console.log variable inside it. In this case, ```num``` will be different each time we call the function ``` for (var i = 0; i < 5; i++) { (function(){ var num = i setTimeout(() => console.log(num), num*1000 ) })() } ``` * async api doesn't have to be pushed into message queue in order, it's depends on time ``` for (let i = 0; i < 5; i++) { setTimeout(() => console.log(i), (5-i)*1000 ) } //async API: console.log(0) with 5s, console.log(1), console.log(2), console.log(3), console.log(4) with 1s //message queue: console.log(4), console.log(3), console.log(2), console.log(1), console.log(0) //4 //3 //2 //1 //0 ``` ### callback hell ``` const foo = () => { console.log('foo') } const getRandomTime = () => { return Math.floor(Math.random()*6) } const callFnInRandomTime = (cb) => { const timer = getRandomTime() console.log(`wait for ${timer}s to call a function`) setTimeout(cb, timer*1000) } //to call a function after calling a function... //callback hell callFnInRandomTime(() => { callFnInRandomTime(() => { callFnInRandomTime(() => { callFnInRandomTime(foo) }) }) }) "wait for 3s to call a function" "wait for 2s to call a function" "wait for 2s to call a function" "wait for 2s to call a function" "foo" ``` ### XHR(XMLHttpRequest) ``` function getUser(cb, id) { let xhttp = new XMLHttpRequest() xhttp.onreadystatechange = function () { if (this.readyState === 4 && this.status === 200) { const data = JSON.parse(xhttp.response) cb(data) } } const url = 'https://jsonplaceholder.typicode.com/posts/' xhttp.open('GET', url + id, true) xhttp.send() } function print(data) { console.log(data) } getUser(print, 5) //get data with id=5 ``` * If we want data in the order 5, 12, 3... Approach 1: callback ``` getUser(data => { print(data) getUser(data => { print(data) getUser(data => { print(data) }, 3) }, 12) }, 5) ``` ### Promise promise is async * usage: resolve ``` const getRandomTime = () => { return Math.floor(Math.random() * 6); } const p = new Promise((resolve, reject) => { const timer = getRandomTime(); console.log(`${timer}s`); setTimeout(() => { resolve('hello'); }, timer * 1000); }) .then(data => { console.log('this is first data: ', data); return data + 2; }) .then(data => console.log('this is second data: ', data)) //"wait for 4s" //"This is first data hello" //"This is second data hello2" ``` ```then``` will receive the data from resolve and call a callback function, and the return will transfer data to the next ```then``` * usage: reject ``` const p = new Promise( (resolve, reject) => { const timer = getRandomTime() console.log(`wait for ${timer}s`) if (timer > 3) { reject({timer: timer, message: 'fail'}) } setTimeout(() => { resolve('hello') }, timer*1000 ) }) .then( data => { console.log(`This is first data ${data}`) return data+2 }) .then( data => { console.log(`This is second data ${data}`) }) .catch(err => console.log(err)) //"wait for 4s" //{ message: "fail", timer: 4 } ``` * replace callback hell In this way, we can print users in our wish order ``` const getUser = id => { return new Promise( (resolve, reject) => { const xhttp = new XMLHttpRequest() const url = 'https://jsonplaceholder.typicode.com/posts/' xhttp.open('GET', url + id, true) xhttp.send() xhttp.onreadystatechange = function () { if (this.readyState === 4 && this.status === 200) { const data = JSON.parse(xhttp.response) resolve(data) } } }) } getUser(5) .then( data => { console.log(data) return getUser(12) }) .then( data => { console.log(data) return getUser(3) }) .then( data => { console.log(data) }) ``` ### async/await v.s. Promise They are the same, async/await is just syntax sugar of promise. It enables asynchronous, **promise-based** behavior to be written in a cleaner style. ``` const getUser = id => { return new Promise( (resolve, reject) => { const xhttp = new XMLHttpRequest() const url = 'https://jsonplaceholder.typicode.com/posts/' xhttp.open('GET', url + id, true) xhttp.send() xhttp.onreadystatechange = function () { if (this.readyState === 4 && this.status === 200) { const data = JSON.parse(xhttp.response) resolve(data) } } }) } const getUsers = async () => { const data5 = await getUser(5) console.log(data5) const data12 = await getUser(12) console.log(data12) const data3 = await getUser(3) console.log(data3) } getUsers() ``` * catch error ``` const getUser = id => { return new Promise( (resolve, reject) => { const xhttp = new XMLHttpRequest() const url = 'https://jsonplaceholder.typicode.com/posts/' xhttp.open('GET', url + id, true) xhttp.send() xhttp.onreadystatechange = function () { if (this.readyState === 4 && this.status === 200) { const data = JSON.parse(xhttp.response) reject({message: 'fail'}) } } }) } const getUsers = async () => { try { const data5 = await getUser(5) console.log(data5) const data12 = await getUser(12) console.log(data12) const data3 = await getUser(3) console.log(data3) } catch (error) { console.log(error) } } //{ message: "fail" } ``` ### Construct Promise Since Promise use the ```new``` keyword, we can construct it using class * resolve ``` class MyPromise { thenCallBackQueue = []; constructor(exe) { exe(this.resolve, this.reject); } resolve = (data) => { const cb = this.thenCallBackQueue.shift(); cb(data); } reject() {} then(thenCallBack) { this.thenCallBackQueue.push(thenCallBack); } catch(catchCallBack) {} } const p = new MyPromise((resolve, reject) => { setTimeout(() => resolve('hello'), 0); }) .then(data => { // console.log('this is data: ', data); }) ``` ``` class MyPromise { thenCallBackQueue = []; currentData; constructor(exe) { exe(this.resolve, this.reject.bind(this)); } resolve = (data) => { setTimeout(() => { this.currentData = data while (this.thenCallBackQueue.length) { const cb = this.thenCallBackQueue.shift() //get the first element if (this.currentData instanceof MyPromise) { this.currentData.then((d) => { this.currentData = cb(d) }) } else { this.currentData = cb(this.currentData) } } }, 0) } reject() { } then(thenCallBack) { this.thenCallBackQueue.push(thenCallBack); return this; } catch(catchCallBack) { } all() {} } const p = new MyPromise((resolve, reject) => { // setTimeout(() => resolve('hello'), 0); resolve('hello'); }) .then(data => { // console.log('this is data: ', data); return new MyPromise((res, rej) => { // setTimeout(() => { res('Dio') // }, 0); }); }) .then(data2 => console.log('this is data2: ', data2)) // .catch(err => console.log(err)); ``` * Note: use arrow function to avoid from using bind * **Promise State** If we console.log Promise on browser, there will be: ``` [[PromiseState]]: "fulfilled" [[PromiseResult]]: undefined ``` There're 3 states of Promise: 1. pending: initial state, neither fulfilled nor rejected. 2. fulfilled: meaning that the operation was completed successfully. 3. rejected: meaning that the operation failed. ![](https://i.imgur.com/cK7iTFW.png) * **Promise all** syntax: ```Promise.all``` will return a new Promise object ``` const p1 = fetch('https://jsonplaceholder.typicode.com/todos/1').then((d) => d.json() ) const p2 = fetch('https://jsonplaceholder.typicode.com/todos/2').then((d) => d.json() ) const p3 = fetch('https://jsonplaceholder.typicode.com/todos/3').then((d) => d.json() ) Promise.all([p1, p2, p3]).then((v) => { console.log(v) }) ``` * build ``` static all(arr) { let counter = 0 const resolveData = [] return new MyPromise((res, rej) => { arr.forEach((el, i) => { el.then((data) => { counter++ resolveData[i] = data if (counter === arr.length) res(resolveData) }) }) }) } ``` *Note: The ```static``` keyword defines a static method or property for a class. It cannot be called on instances.* ### Array methods * push * shift * pop * unshift difference: performance if dequeue a lot: use pop+unshift if enqueue a lot: use shift+push ### Fetch structure: ```fetch(url, object)``` return: a Promise ``` function myFetch(url, options) { return new Promise((res, rej) => { var xhttp = new XMLHttpRequest() xhttp.open(options.method, url, true) if (options.headers) { for (let key in options.headers) { xhttp.setRequestHeader(key, options.headers[key]) } } xhttp.onreadystatechange = function () { if (this.readyState === 4) { if (this.status >= 200 && this.status < 300) { const response = { json: () => JSON.parse(xhttp.response), } res(response) } else { rej({ readyState: this.readyState, errorStatus: this.status }) } } } xhttp.send(options.body) }) } //usage myFetch('https://jsonplaceholder.typicode.com/postsasdas', { method: 'POST', body: JSON.stringify({ title: 'foo', body: 'bar', userId: 1, }), headers: { 'Content-type': 'application/json; charset=UTF-8', }, }) .then((response) => response.json()) .then((json) => console.log(json)) ``` --- ## React 1. just an ui-library 2. one way data binding ### library v.s. framework **What is library?** Just help you to solve a certain problem, React only handle the view part, can develop with many different libraries with developers' own rule **Example** React, JQuery, lodash, underscore, three.js, etc **What is framework?** Will contain several libraries, providing a standard rule to do everything, developers have to follow the rules **Example** Vue, Angular, Express, koa2 > ### Since React is just a view library, we still need other tools to do development. --- ### Ways of creating React project 1. **boilerplate**: `npx create-react-app` 2. **CDN**(content delivery network): put at the end of body Online resources providers that can send you static resources based on your location. Can also help your website optimize the website performance. > ### How to create a React project with CDN, instead of boilerplate? Importing `React` & `ReactDOM` as global object ``` <body> <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> </body> ``` ### React v.s. ReactDOM * **React**: generates virtual DOM, including methods like `createElement`, which is used when tranforming JSX to JS * **ReactDOM**: An api, helps you touching the real DOM, by comparing the virtual DOM with real DOM, and updating real DOM There're many methods of ReactDOM, including render ### Virtual DOM v.s. (HTML)DOM ``` <button class="add">Add</button> <ul class="list"></ul> <script> const btn = document.querySelector('.add') const ul = document.querySelector('.list') const list = ['Patric', 'Sam', 'Jane'] btn.addEventListener('click', () => { list.push('newName') ul.innerHTML = list.map((name) => `<li>${name}</li>`).join('') }) ul.innerHTML = list.map((name) => `<li>${name}</li>`).join('') </script> ``` **Issues of html DOM** Follows the MVC pattern, once data change, the ui change. Therefore, in this example, everytime we add a new item to the list, it'll re-generate all `li`. It's hard for traditional DOM to find the best way to update your DOM, which is, only create the new `li` element **Why virtual DOM?** React uses the `diff` algorithm to compare the previous virtual DOM with next virtual DOM, find the best way to update it **More Info** https://reactkungfu.com/2015/10/the-difference-between-virtual-dom-and-dom/ --- ### JSX -> pure JS ❌ JSX cannot be understood by JS ``` class HelloMessage extends React.Component { render() { return <div>Hello {this.props.name}</div> //error here } } ReactDOM.render( <HelloMessage name="Taylor" />, document.getElementById('hello') ) ``` ✨ JSX: A syntax sugar, looks like JS+HTML, need compiler(ex. babel) to transpile it into JS, let the browser understand it. ✨ Babel can help us transpile JSX to JS with the function `createElement` ⛏ Replace the JSX with `createElement` method in `React` object ``` //before <div>Hello {this.props.name}</div> //after React.createElement('div', null, 'Hello ', this.props.name) ``` ``` //before <HelloMessage name="Taylor" /> //after React.createElement(HelloMessage, { name: 'Taylor', }) ``` https://zh-hant.reactjs.org/docs/introducing-jsx.html --- ### web component self-definition tag Nothing to do with React https://developer.mozilla.org/en-US/docs/Web/Web_Components --- ### npm #### `package.json` ``` { "name": "notes", "version": "1.0.0", "description": "", "main": "index.js", //entry point of the project "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } ``` #### npm script * For default scripts, `npm run <script>` & `npm <script>` both works. * For custom scripts, we have to follow the original command `npm run <script>` #### install packages `npm install --save-dev @babel/core @babel/cli` The `--save-dev` means it'll install packages as `devDependencies`, which will not be installed in production. #### `^7.12.13` The `^` i means above which version #### `package-lock.json` * Why this file? Generally, we might not install the same version simply with the same command `npm install --save-dev @babel/core @babel/cli` for now and one year later. * Solution To help developers use the same package versions in a project, with `package-lock.json`, we can install the same versions just run `npm install` --- ❓ It's troublesome to write pure JS for React everytime, is there a way to transform JSX to JS for us? ❗ Here comes the Babel --- ### Setup the Babel: transpile JSX to JS Follows the steps on https://babeljs.io/setup#installation 1⃣ **Install Babel cli** ``` npm install --save-dev @babel/core @babel/cli ``` 2⃣ **Set npm scripts**: instead of running babel directly(babel is not a script in cli) - Issue When we run the script `"build": "babel src -d lib"` It shows the error: ``` Add @babel/preset-react (https://git.io/JfeDR) to the 'presets' section of your Babel config to enable transformation. ``` - Solution: add preset **What is presets?** Let Babel know what kind of JS code Babel is going to transpile. Ex. preset for react, preset for ES6, etc. 3⃣ **Set preset:** 1. Run npm install --save-dev @babel/preset-react 2. Create a babel.config.json file 3. Add the following into json file ``` { "presets": ["@babel/preset-react"] } ``` After build, we can see the transformed js in lib folder. --- ### Structure(after using babel) - `src` folder The `npm run build` will transpile contents in `src` to `lib` folder - `lib` folder Contains files that's generated by Babel - `public` folder 1. include`index.html` 2. put CDN inside this file 3. set the `id=root` div 4. import `index.js` from `lib` ``` //index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="root"></div> <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js" ></script> <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" ></script> <script src="../lib/index.js"></script> </body> </html> ``` Here's an error `Uncaught SyntaxError: Cannot use import statement outside a module` related to `import Home from './components/Home'` **This is because `import` is a method in node.js not browser.** There's actually the `import` syntax in ES6, but not widely used https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/import --- ### NodeJS vs JS vs EcamScript * EcamScript: syntax standard(ex. ES6) * JS: EcamScript + Web API(ex. DOM) in browser: ``` console.log(this) >>> Window {window: Window, self: Window, document: document, name: "", location: Location, …} //these are web apis ``` * NodeJS: EcamScript + Node API in terminal: ``` console.log(this) >>> Object [global] {...} ``` > ### The problem of cannot using node method in browser, is called bundling, and we can solve it with webpack. **What is bundling?** Bundling merges multiple JavaScript files together into one file. https://medium.com/@gimenete/how-javascript-bundlers-work-1fc0d0caf2da And webpack can do more than bundling. --- ### Standards of using JS Modules This standard has been adopted by NodeJS, so it's widely seen in NodeJS projects. However, it's not supported by the browser. That's why we need webpack or Browserify. * CommonJS | Use`require` and `module.exports` two ways of exporting: **named** and **default exports** ``` //default export const a = 5 module.exports = a //named export module.exports.a = 5 ``` two ways of importing ``` // named import without changing the name a const { a } = require('...') // named import changing the name into b const { a: b } = require('...') // Default import const a = require('...') ``` * ES Modules(ESM) | Use `import` and `export` two ways of exporting: **named** and **default exports** ``` //default export export const a = 5 //named export export default a ``` two ways of importing ``` // named import without changing the name a const { a } = require('...') // named import changing the name into b const { a: b } = require('...') // Default import const a = require('...') ``` CommonJS is the native module system for NodeJS, but ESM is not. If we try to use ESM, it'll show error like this: ``` SyntaxError: Cannot use import statement outside a module ``` To use ESM, there're few solutions: 1. Change the file extension from `.js` to `.mjs` 2. Set `"type": "module"` in package.json ??? https://auth0.com/blog/javascript-module-systems-showdown/ https://medium.com/computed-comparisons/commonjs-vs-amd-vs-requirejs-vs-es6-modules-2e814b114a0b https://blog.huli.tw/2020/01/21/webpack-newbie-tutorial/ https://dev.to/exacs/node-js-12-supports-es-modules-do-you-know-the-difference-between-commonjs-and-es2015-modules-hg9 --- ### Setup the Webpack: in order to use NodeJS module Ex. minify, uglify, polyfill **1. create config file** Under your project folder, create a file `webpack.config.js` with the content copy from https://webpack.js.org/ ``` const path = require('path') //what is the path here? module.exports = { entry: './lib/index.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js', }, } ``` * The path in line 1 helps us generate the path, which is natively supported by NodeJS, but cannot be used in the browser * **Which `index.js` file should be the entry point?** The order should be : babel -> bundle!! Therefore, we should use the after babling file `lib/index.js` as entry point, not `src/index.js` * The output content will be put at `dist/bundle.js` **2. install webpack cli** ``` npm install webpack webpack-cli --save-dev ``` So that we can run `webpack ...` in shell **3. Set webpack cli in `package.json`** ``` "build-webpack": "webpack --config webpack.config.js" ``` > ### Now finally see the result! But, what if we want to remove the CDN in the `index.html`? --- ### import React&ReactDOM instead of CDN First, we have to install react&react-dom modules with node ``` npm i react react-dom ``` Second, we have to import React & ReactDOM to corresponding files **For the root file `index.js`:** ``` import Home from './components/Home' import ReactDOM from 'react-dom' import React from 'react' ReactDOM.render(<Home name="Taylor" />, document.getElementById('root')) ``` Clearly, we need `ReactDOM` here. We also need `React` because the `<Home>` tag has to be tranformed to `React.createElement(Home, { name: "Taylor" })` **For child component `Home.js`:** ``` import React from 'react' class Home extends React.Component { render() { return <div>Home {this.props.name}</div> } } export default Home ``` --- ### Structure(after using webpack) We use webpack to solve the issue of cannot use `import Home` in JS, and install `react`&`react-dom` libraries so that we don't have to use CDN - `src` folder: `npm run build-babel` will transpile contents in `src` to `lib` folder - `lib` folder: contains files that's generated by Babel - `dist` folder: contains `bundle.js` after execute `npm run build-webpack` - `public` folder 1. remove the CDN inside this file 2. instead of importing `lib/index.js`, importing `dist/bundle.js` --- ### webpack loader * SASS/SCSS We can use loader in webpack to tranpile everything including SASS to CSS. * Babel With webpack loader, we don't need to setup babel anymore, we can just install babel-loader --- > ### This is why React is just a library, React only provide React&ReactDOM object, we still need like webpack to start a React project. --- ### AJAX | SPA | JSON * AJAX **What is AJAX?** Allows websites change the DOM **without refreshing the page**, only data transferring. **Benefit of AJAX** Makes SPA websites possible. **Where can we see it?** In the browser developer's inspect, the network section, we can see the *XHR(XMLHttpRequest)*, when we use AJAX techniques to send data, it'll show some data transfer there. * SPA **What is SPA?** Single Page Application. Enables the application to have only one html file, and looks like changing pages using only JS. **Why do we need SPA?** A SPA website doesn't have to refresh(get the whole page from server) everytime we change the url, which reduces the delay. **Example** MPA(Multi Page Application): https://about.google/ SPA(Single Page Application): https://reactjs.org/docs/getting-started.html * JSON **What is JSON?** A key-valued data type, full name is JavaScript Object Notation. **Why do we prefer to use JSON to transfer data, instead of using XML?** 1. light 2. natively supported by JS We can use `JSON.parse` to transform data --- ### `.jsx` v.s. `.js`? When React just released, it's recommended to use `.jsx` file to indicates that in this file, it's using JSX syntax, and so when the babel transpiling files, it'll know `.jsx` file needs JSX transform and `.js` file doesn't. But as it getting more mature, we don't have to use the doc name to distinguish them, the babel will read the file and when it sees JSX syntax, it'll know it's a JSX file. --- ### CRA(Create React App) We've introduced how to use CDN, babel, webpack to setup a react project. An easier way is to use the one-line-command `npx create-react-app my-app`. #### What is `npx` ? `npx` is a package runner, different from `npm` which only helps you installing packages. `npx create-react-app` will firstly install the `create-react-app` and then remove it. Therefore, `create-react-app` is not a global command, and we only need to run it once. After the react project created, we can see the structure like this: - `public` folder: contains `index.html`, `favicon.ico`, `manifest.json`, `robots.txt` etc. #### manifest.json A summary of the web app #### robots.txt Define how we dealing with robots on the internet(ex. web crawler), like defining which files cannot be accessed... #### `react-scripts` After we run `npm start`, it'll run the command start script defined inside `package.json` ``` //package.json "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" } ``` As we knew, a react project should use babel, and it's included inside `react-scripts`. If we open the `react-scripts` folder in node_mosuldes folder, inside the `package.json` file, we can see babel is a dependency of this module. ``` "dependencies": { "@babel/core": "7.12.3", ``` > #### The react-scripts module automatically includes many dependencies for us, but what if we want to customize our own dependencies? #### `eject` We can use the `eject` script defined in the package.json. After running `npm run eject`: 1. new folders `config` and `scripts` are generated 2. cannot go back to original react project generated by the CRA 3. we can still run `npm start` to start the react project, but the actual command is different, it's using `node` rather than `react-scripts` as before ``` //package.json after ejected "scripts": { "start": "node scripts/start.js", "build": "node scripts/build.js", "test": "node scripts/test.js" }, ``` --- ### Structure your project There're two common ways: **1. By Function** - components - - Counter - - - Counter.js - apis - models - utils some reusable helper methods, ex. string manipulation - styles every css or scss files here should be globally, specific component style should be put inside each component folder **2. By Feature** - ToDo - - put every component, api, ..., related to ToDo feature inside this folder - Counter --- ### Atomic design Make components more reusable --- ### export ways 1. export default ``` export default import Template from './components/templates' ``` 2. named export ``` export { Template, AnotherTemplate } import {Template, AnotherTemplate} from './components/templates' ``` --- ### Fragment Since JSX element must have root node, we can use `React.Fragment` as a root node. The benefit of using Fragment instead of `div` is, when we designing the css, there'll not have an unexpected node. ``` <React.Fragment> <div></div> <button></button> </React.Fragment> ``` Or using the new syntax sugar `<></>`: ``` <> <div></div> <button></button> </> ``` --- ### Lifecycle https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/ ![](https://i.imgur.com/XakHj6a.png) 1. Mounting **What does this phase do?** To initialize the react structure, the virtual DOM, previous DOM **Process** constructor -> getDerivedStateFromProps -> render -> componentDidMount **Where can we get element?** If not yet exists, get `null` constructor(`null`) -> getDerivedStateFromProps(`null`) -> render(`null`) -> componentDidMount(not `null`) **render** the render method is helping us to generate the new virtualDOM **componentDidMount** Once the component has been mounted, this method will be triggered, and we can touch the real DOM Usually we'll put data manipulation like `fetch`. Because after this method, the react is ready for updating 2. Updating **What does this phase do?** If we want to change the UI using the state **How to trigger the update?** * new props If we pass data using `props`, when the `props` changes, the component will update * setState If we call `setState` in the component * forceUpdate React is using prototype chain, and `forceUpdate` is a method of class components' parent. Therefore, to call this method, we just need to call `this.forceUpdate` ![](https://i.imgur.com/9KuHPGk.png) **Process** getDerivedStateFromProps -> shouldComponentUpdate -> componentDidUpdate * getDerivedStateFromProps Tansform props that passing into this component into state #### React will use recursion to generate the virtual DOM. So, the most child will be triggered to the end in the parent render method, and then parent goes to the end. Suppose `Layout` is a child of `App` ``` //App component render() { console.log('App - render') return ( <Layout content="Page1|Page2"> <Counter></Counter> </Layout> ) } ``` The process of lifecycle would be like this: ``` App - construct App - getDerivedStateFromProps App - render Layout - construct Layout - getDerivedStateFromProps Layout - render Layout - componentDidMount App - componentDidMount ``` * shouldComponentUpdate When the parent compnent update, it'll trigger the update of child component, but it's redundant and reduce the performance. Without `shouldComponentUpdate`, the process will be: ``` update App - getDerivedStateFromProps App - render Layout - getDerivedStateFromProps Layout - render ``` After we defined the `shouldComponentUpdate` method: ``` update App - getDerivedStateFromProps App - render Layout - getDerivedStateFromProps Layout - shouldComponentUpdate ``` If we go to the source code of `shouldComponentUpdate`, we can see there're few parameters: `nextProps`, `nextState`, `nextContext` ``` shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean; ``` Let's try to compare the values of currentProps/nextProps and currentState/nextState ``` shouldComponentUpdate(nextProps, nextState) { console.log(nextProps === this.props) //false console.log(nextState === this.state) //true } ``` Since we haven't defined the state, it's `null`. And since we're passing props, it's an object, but object is not comparable, so the equality will always be false. We can compare the property of props. The return boolean value will determine whether the component should be updated. - `true`: update -> render again - `false`: do not update -> do not render Therefore, we can set only if the current value inside the props is different from previous props value, do we update this component. ``` //Layout component shouldComponentUpdate(nextProps, nextState) { return nextProps.content !== this.props.content } ``` --- #### Component vs PureComponent They're basically the same. But, `PureComponent` will automatically generate the `shouldComponentUpdate`, and do the shallow(react use shallow copy) comparison between nextProps/this.props and nextState/this.state. --- * componentDidUpdate If the component did update, it'll end with this method 3. Unmounting **Why do we need this phase?** If we don't do unmounting, it'll cause memory leak, that means, any JS objects, functions inside this component will not be clean up, and functions like `setInterval` will continue to run. ``` //App //Initialize a state: test this.state = { test: null, } render() { console.log('App - render') return ( <Layout content="Page1|Page2"> <button onClick={() => { this.setState({test: 'test'}) }}> Update </button> //the Counter comopnent will be unmounted after we click the update button {!this.state.test ? <Counter></Counter> : null} </Layout> ) } //Counter componentWillUnmount() { console.log('Counter - unmounting') } ``` **Notice**: if we keep the return value of `shouldComponentUpdate` inside the parent component of `Counter` as `false`, the `Counter` component will not be updated. If we clicked the update button, we can see that `Counter - unmounting` shows up after the `Layout - render`. *What if we don't do anything inside the `componentWillUnmount` when there's some functions like `setInterval`?* ``` //Counter componentDidMount() { setInterval(() => { this.setState({ counter: this.state.counter + 1, }) console.log('interval triggered') }, 1000) } ``` Even the `Counter` component is unmounted, the `console.log('interval triggered')` is kept triggerring. To solve this issue, after we unmount the component(in the `componentWillUnmount`), we have to call `clearInterval`. ``` //Counter componentDidMount() { this._intervalID = setInterval(() => { this.setState({ counter: this.state.counter + 1, }) console.log('interval triggered') }, 1000) } componentWillUnmount() { clearInterval(this._intervalID) } ``` --- ### data binding React can do both one-way data binding and two-way data binding, but it's using **one-way data flow**. ``` <> <h1>Counter: {this.state.counter}</h1> <button onClick={this.handleAdd}>Add</button> </> ``` This click event implementing a two-way data binding. From view to model(click button, change state), and model to view(according to state, change counter value on the screen) https://tkssharma.gitbook.io/react-training/day-01/react-js-3-principles/one-way-data-flow In React JS, data flows in one direction, from **Parent to Child** #### props - parent to child 1. We can define props name on the component ``` //in the parent component <Layout content='Page1'></Layout> //in the Layout component this.props.content //Page1 ``` 2. Anything between the child component tag is also props called `props.children` ``` //in the parent component <Layout content='Page1'>Hello</Layout> //in the child component this.props.children //Hello ``` #### How to pass from child to parent? We can lift up the states and using some state management tool like `Redux`, `ContextAPI` * Redux: help us create global state which can be shared between components #### view to model Using event bind ``` handleUserInputChange = (e) => { this.setState({ userInput: e.target.value, }) } ``` #### model to view Using state and value ``` <input className="todolist-inputbar" placeholder="What are you going to do?" value={this.state.userInput} onChange={this.handleUserInputChange} ></input> ``` *This achieved two-way data binding.* --- ### Class Component **state** We can directly define state inside the class (it's supported by the JS method `_defineProperty`). ``` class Counter extends Component { state = { counter: 0 } render() { return <h1>Counter: {this.state.counter}</h1> } } ``` But, usaually, we use constructor inside class component to define the state ``` class Counter extends Component { constructor(props) { super(props) this.state = { counter: 0, } } render() { return <h1>Counter: {this.state.counter}</h1> } } ``` #### super(props) We use `super(props)` to invoke the parent constructor `React.Component`, so that the child component can use all the properties of the parent constructor. --- **`this.setState` is an async method** If we run `this.setState` twice, trying to adding 2 onto the counter... ``` //react method handleAdd() { this.setState({ counter: this.state.counter + 1, //this.state.counter is 0 here }) this.setState({ counter: this.state.counter + 1, //this.state.counter is still 0 here }) } ``` It'll still add only 1 onto it. Let's mimic an async setState method: ``` //pure JS let component = { state: { counter: 0 } } function setState(newState) { setTimeout(()=>{ component.state = newState }, 0) } setState({ counter: component.state.counter + 1 }) //component.state.counter here is 0 console.log(component.state.counter) >>> 0 setState({ counter: component.state.counter + 1 }) //component.state.counter here is 0 console.log(component.state.counter) >>> 0 ``` **How to solve this issue?** Since `setState`is async, we can pass a *callback function* into it. ``` //react method handleAdd() { this.setState((preState) => { return { counter: preState.counter + 1, //this.state.counter is 0 here } }) this.setState((preState) => { return { counter: preState.counter + 1, //this.state.counter is 1 here } }) } ``` Since `setState` is an async method, if we directly `console.log` after `serState`, we'll not see the updated state. Therefore, we have to make `console.log` async as well using `setTimeout`. ``` //pure JS let component = { state: { counter: 0 } } function setState(newState) { //if we're passing callback function into setState if (typeof newState === 'function') { setTimeout(()=>{ component.state = newState(component.state) }, 0) } else { //if we're just passing new state setTimeout(()=>{ component.state = newState }, 0) } } setState((preState) => { return { counter: preState.counter + 1, } }) setTimeout(() => { console.log(component.state.counter) },0) setState((preState) => { return { counter: preState.counter + 1, } }) setTimeout(() => { console.log(component.state.counter) },0) ``` --- #### `this` in class component What's the correct way to define functions inside class component? * Will this function correctly work? ``` handleAdd() { this.setState({ counter: this.state.counter + 1, }) } ``` => No, this code will cause error. Because `this` is `undefined` * Solution 1. use bind `this.handleAdd = this.handleAdd.bind(this)` 3. arrow function --- ### class component vs functional component * class component: like a container, helps you to manage states * functional component: helps you do the presentation --- ### HOC **Why do we need HOC?** Reuse lifecycle logic **syntax** * `parameter`: pass in an original component * `return`: we can return a new component ``` //hoc component: withTodos export const withTodos = (WrappedComponent) => class extends Component { return ( <WrappedComponent count={this.state.todos.length} todos={this.state.todos} addTodo={this.handlAddTodo} removeTodo={this.handleRemoveTodo} {...this.props} ></WrappedComponent> ) } //in original component export default withTodos(OriginalComponent) ``` * Get data that's passed from parent to original child If we want to get data from parent using props, it'll not work because the original child component is not export as original component, it's modified by HOC. Therefore, we have to get data inside HOC, then pass it into the original child. Using `{...this.props}` allow us to pass the whole props. ``` //Parent <TodoList title="My Todo List"></TodoList> //Original child <header className="todolist-header">{this.props.title}</header> //HOC component ``` **Disadvantage of HOC** Without looking into the source code, we cannot know what will a HOC returns. When there're many HOCs, that'll cause issues. ``` export default withTodos(TodoList) //we have no idea what does withTodos do ``` --- ### HOC v.s. Render Props --- ### Render Props Using a `render` callback function to transfer data, it's just a concept. **logic** 1. Create a new component `WithTodoData` 2. Move all methods inside HOC into `WithTodoData` 3. Render props can use a props called `render` to pass a function 4. We can use the function using `this.props.render()` 5. To pass data/function from render props component to original component, we can pass parameters inside `render` in RenderProps component, and setup them as props in the parent component. ``` //App component <WithTodoData render={(todos, addTodo, removeTodo) => ( <TodoList todos={todos} addTodo={addTodo} removeTodo={removeTodo} ></TodoList> )} ></WithTodoData> ``` ``` //Render Props component class WithTodoData extends Component { //everything here is the same as in HOC// render() { return this.props.render( this.state.todos, this.handlAddTodo, this.handleRemoveTodo ) } } export default WithTodoData ``` And actually, we don't even need to use the `render` props, we can just use the default props `children` ``` //App <WithTodoData> {(todos, addTodo, removeTodo) => ( <TodoCount title="My Todo Count" count={todos.length}></TodoCount> )} </WithTodoData> ``` ``` //Render Props component render() { return this.props.children( this.state.todos, this.handlAddTodo, this.handleRemoveTodo ) } ``` **Benefit** 1. Render Props can also be reused in other component just like HOC 2. A more benefic is, we can directly understand what happened just by looking at the parameters of render **Notice** The children of props that's passed into render props component must be a callback function, not just an object. Because we're using `this.props.children()` to use this props. --- ### coupling & decoupling Suppose we've implemented another component that has the same structure but different style from our `Todo` component, let's call it `ColorTodo`. We can just replace the `Todo` component with `ColorTodo` in `TodoList` component. ``` //TodoList <ul className="todolist-items"> {this.props.todos ? this.props.todos.map((todo) => ( <ColorTodo key={todo.id} todo={todo} handleRemoveTodo={() => removeTodo(todo.id)} ></ColorTodo> )) : null} </ul> ``` ``` //App <WithTodoData render={(todos, addTodo, removeTodo) => ( <TodoList title="My Todo List" todos={todos} addTodo={addTodo} removeTodo={removeTodo} ></TodoList> )} ></WithTodoData> ``` The issue is, now `Todo`&`ColorTodo` is binding inside `TodoList`, but what if we want to have different color types of TodoList, like black TodoList, which TodoList, colorful TodoList... If `Todo` is binded inside `TodoList`, we can only have one type of TodoList. To solve this issue, we can decoupling `Todo` with `TodoList`. **coupling & decoupling?** ``` //coupling foo() { foo2() } //de-coupling foo(cb) { cb() } foo(foo2) ``` **Re-construct** First, use render props concept in `TodoList`. ``` //TodoList <ul className="todolist-items"> {this.props.children(this.props.todos, this.props.removeTodo)} </ul> ``` Then, put `Todo` as children of props in `App`. ``` //App <WithTodoData render={(todos, addTodo, removeTodo) => ( <TodoList title="My Todo List" todos={todos} addTodo={addTodo} removeTodo={removeTodo} > //use callback function as props' children here {(todos, removeTodo) => todos ? todos.map((todo) => ( <ColorTodo key={todo.id} todo={todo} handleRemoveTodo={() => removeTodo(todo.id)} ></ColorTodo> )) : null } </TodoList> )} ></WithTodoData> ``` In this way, if we want another `TodoList` with `Todo` component, we can just create another `TodoList`, and use `Todo` inside the component. --- ## Redux ### Intro to Redux **What is Redux?** A tool that helping you to manipulate the data flow. #### Reducer ``` function counterReducer(state = { value: 0 }, action) { switch (action.type) { case 'counter/incremented': return { value: state.value + 1 } case 'counter/decremented': return { value: state.value - 1 } default: return state } } ``` * 2 parameters: 1. state: `{ value: 0 }` is the initial state 2. action: an object * return a new state `{ value: state.value + 1 }` #### store The return object of `createStore` method. An object tree in Redux. A store is a state container which holds the application’s state. **Redux can have only a single store in your application**. Whenever a store is created in Redux, you need to specify the reducer. ``` let store = createStore(counterReducer) ``` store has 3 methods: 1. `subscribe`: listen to the state change. If dispatch is triggered, the callback function in subscribe will be triggered once dispatch finished. subscribe is not going to trigger the callback function. It just save the cb and trigger it after dispatch. input: any callback function ``` store.subscribe(() => console.log(store.getState())) //this will print current state everytime we dispatch store.subscribe(() => console.log('hello')) //this will print hello everytime we dispatch ``` 2. `getState`: get currentState 3. `dispatch`: change state inside the store. Take an action -> send the action to reducer -> return newState to store input: an action **Reducer** We can create any reducer as we want ``` function counterReducer(state = { value: 0 }, action) { switch (action.type) { case 'counter/incremented': return { value: state.value + 1 } case 'counter/decremented': return { value: state.value - 1 } default: return state } } ``` Reducer is a pure function. **pure function** 1. No side effect(have control to everything inside the function) 2. data imutable: cannot change the input data such as push data into the input. All the input data should be read-only. 3. same input same output **Pure Components** Pure Components in React are the components which do not re-renders when the value of state and props has been updated with the same values. Ex. ``` //this is not a pure function, because add2 is not controlled by this function function add(a) { return add2(a)+a } //this is a pure function function add2(b) { return b } ``` **action creater** A function that'll return an action object. --- > Issue: we use `Counter` component in different components: App & Header. However, the value does not change simultaneously. When we click add button at App, the Counter value at App changed, but Header's Counter remains the same. If we want the two Counter use the same value... > Which means, if we want to share data in different component, how could we do that? Use Redux! ### Use Redux in components ``` import { store, actionCreater } from '../../Redux/Redux' ... render() { return ( <> <h1>Counter: {store.getState().value}</h1> <button onClick={() => store.dispatch(actionCreater.incremented())}> Add </button> </> ) } ``` #### Update UI *Issue:* state does change, but UI is not updated. Because usually, we use `setState` to update the UI, but we don't have state here. Therefore, to update the UI here, we have to use `forceUpdate` in the lifecycle's update phase since we don't have props nor states here. And since it only have to be triggered once, in lifcycle, `componentDidMount` is the one that'll only be triggered once. ``` componentDidMount() { store.subscribe(() => this.forceUpdate()) } ``` **forceUpdate in componentDidMount is repeated** We can use HOC/render props to make this duplicated code reusable. --- ### React-Redux Use to connect React with Redux. With React-Redux, we don't have to manually update(call subscribe) to re-render our UI, because react-redux help us do getState|dispatch|subscribe. **connect()** The `connect` function in react-redux is hoc. It's using currying. ``` export default connect(mapStateToProps, mapDispatchToProps)(Counter) ``` * mapStateToProps: map global(store) state to Counter props ``` //the passed in state is a global state const mapStateToProps = (state /*, ownProps*/) => { //thing return here will be the props to Counter return { counter: state.counter, } } ``` * mapDispatchToProps: map store dispatch to Counter props This can be an object or method. Object: ``` const mapDispatchToProps = { increment, decrement, reset } ``` Method: ``` const mapDispatchToProps = (dispatch /*, ownProps*/) => { return { add: () => dispatch(actionCreater.incremented()), } } ``` Looks like using react-redux, we can ignore the store, directly use methods of store. **Modify our component using connect** ``` //Counter <> <h1>Counter: {this.props.counter}</h1> <button onClick={() => this.props.add()}>Add</button> </> const mapStateToProps = (state /*, ownProps*/) => { return { //the 'value' is a state name defined in Redux counter: state.value, } } const mapDispatchToProps = (dispatch /*, ownProps*/) => { return { add: () => dispatch(actionCreater.incremented()), } } ``` **Provider** A context API to provide you a global variable --- ### Funtional Programming | OOP Programming React is using both. Class component is OOP concept. JSX is using Functional Programming. If using Hook&funciton all the time, it's more like functional programming. * Funtional Programming: variables inside here should be immutable, so we usually use `const` to declare them. --- ### React-Router --- ### Context | Props | State --- ## React Hook After React version 16.8. ### useEffect In traditional React, we cannot use lifecycle method inside functional component. In order to use lifecycle, `useEffect()` will be triggered once React has updated the realDOM. `useEffect` is something like `componentDidMount`. However, it's not really like `componentDidMount` that'll only be triggered once, it'll be triggered twice with lifecycles didMount&didUpdate. > #### If we want to trigger it only related to didMount? Use ***dependency array*** of useEffect! In this situation, useEffect will only be triggered when the dependency array is changed. ``` useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // Only re-run the effect if count changes ``` So, to make it trigger only at mount phase, we can just pass `[]` as dependency array, which will never be changed, and thus it'll only run once. #### How can we have one useEffect for mount and another for only update? ``` //create a ref to know whether it's first mount let mountRef = useRef(false) useEffect(() => { console.log('mount') }, []) //this can make sure it only trigger after data is updated useEffect(() => { if (!mountRef.current) { mountRef.current = true } else { console.log('update') } }) ``` ### useState It's using destructure. *input*: initial value, if put nothing, it's undefined *return*: array ``` const [counter, setCounter] = useState(0) //is equal to const result = useState(0) const counter = result[0] const setCounter = result[1] ``` two ways of initializing state: ``` //But this one is better because more flat(less nested) const [counter, setCounter] = useState(0) const [isLoading, setIsLoading] = useState(true) //is basically equal to const [counterObj, setCounterObj] = useState({ counter: 0, isLoading: false, }) ``` However, later on when we want to change only `counter` value and we call `setCounterObj({isLoading: false})`, the `counter` property will be removed, it'll not automatically copy the `counter` property for us like what `setState` does. ### How Hooks works Here's a tricky example ``` //hook component import { useEffect, useState } from 'react' export function HookApp() { const [count, setCounter] = useState(0) return ( <section className="hooksApp"> <h1>Hook App</h1> <div>count: {count}</div> <button onClick={() => setCounter(count + 1)}>Add One</button> <button onClick={() => setTimeout(() => { alert(count) }, 3000) } > Alert </button> </section> ) } //class component import React from 'react' export class ClassApp extends React.Component { state = { count: 0 } render() { return ( <section className="classApp"> <h1>Class App</h1> <div>count: {this.state.count}</div> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Add One </button> <button onClick={() => setTimeout(() => { alert(this.state.count) }, 3000) } > Alert </button> </section> ) } } ``` If we click Alert button and add one for both components, after 3 secs, the class component will alert 1 for the count but hook component will alert 0. **Why?** class component's initial phase will be triggered only once, after that, we have the instance. Therefore, when we call `this.state`, we're refering to the state, with updated value. But functional component with hooks will invoke JSX to re-generate the whole initial component everytime when we change data. Everything inside the hook component is immutable. Solution1: create a global variable Solution2: useRef ### useRef Help us share data between hooks What useRef return will be the previous state's hook value ``` const [counter, setCounter] = useState(0) let counterRef = useRef(counter) console.log(counterRef) >>> {current: 0} //to make counterRef change with counter counterRef.current = counter ``` In this way, we can save data into ref, and it'll not be refreshed everytime data changes. --- ## Flux architecture pattern --- ## TypeScript React was using propType type system. ---