├── .gitattributes ├── .gitignore ├── README.md ├── articles ├── 2023-learnings │ └── index.mdx ├── All you need to know about line breaks.md ├── All you need to know about pixel.md ├── All you need to know about white space in html, css and DOM.md ├── An Utility to group items in order.md ├── An efficient way to check linked list for palindrome.md ├── CSS position fixed is NOT always relative to viewport.md ├── CSS variables.md ├── Centering in CSS.md ├── Checking the existence of a variable.md ├── Controlled and uncontrolled component design pattern in React.md ├── DRY principle.md ├── Deep dive into SyntheticEvent in React.md ├── Deep dive into URL.md ├── Encode URI.md ├── Function sleep in JavaScript.md ├── Generic Programming.md ├── Get the width of an element.md ├── How href attribute of the anchor element gets me stuck.md ├── How to format code in browser.md ├── Implement setInterval with setTimeout.md ├── Introduction to ssh.md ├── Minimum font size.md ├── Mixed content.md ├── Nginx Buffer Problem.md ├── Notes from airbnb.md ├── Path related APIs.md ├── Polishing CSS through building a compound input.md ├── React Hooks - The Ins and Outs.md ├── React optimization tips.md ├── Security risk for opening new tabs or windows.md ├── Spread props trap in JSX.md ├── Sticky footer.md ├── Switch statement.md ├── Tedder - a scrum git branch manager.md ├── Transition, transform and animation.md ├── Two Css properties you may NOT know.md ├── Typescript introduction(IV).md ├── Typescript introduction(Ⅰ).md ├── Typescript introduction(ⅠII).md ├── Typescript introduction(Ⅱ).md ├── Understanding react key.md ├── Ways to iterate through objects.md ├── When and why would I want to use SFC declaration.md ├── Why third-party cookies are NOT sent where you think they should.md ├── Write once run anywhere with sharing components.md ├── about-me │ └── index.mdx ├── all-about-my-development │ └── index.mdx ├── git-config │ └── index.md ├── my-ts-cheat-sheet │ └── index.mdx ├── my-vim-cheat-sheet │ └── index.mdx └── resources │ └── index.mdx └── images ├── rn-android.png ├── rn-ios.png ├── rn-structure.jpg ├── rn-web.png └── tedder.png /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=JavaScript 2 | *.css linguist-language=JavaScript 3 | *.md linguist-language=JavaScript -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 🚚 Personal Blog. 2 | 3 | - [Ways to iterate through objects](https://github.com/n0ruSh/blogs/blob/master/articles/Ways%20to%20iterate%20through%20objects.md) 4 | - [Step over nginx buffer issue](https://github.com/n0ruSh/blogs/blob/master/articles/Nginx%20Buffer%20Problem.md) 5 | - [Tedder - a scrum git branch manager](https://github.com/n0ruSh/blogs/blob/master/articles/Tedder%20-%20a%20scrum%20git%20branch%20manager.md) 6 | - [Security risk for opening new tabs or windows](https://github.com/n0ruSh/blogs/blob/master/articles/Security%20risk%20for%20opening%20new%20tabs%20or%20windows.md) 7 | - [DRY principle](https://github.com/n0ruSh/blogs/blob/master/articles/DRY%20principle.md) 8 | - [Write once run anywhere with sharing components](https://github.com/n0ruSh/blogs/blob/master/articles/Write%20once%20run%20anywhere%20with%20sharing%20components.md) 9 | - [Generic Programming](https://github.com/n0ruSh/blogs/blob/master/articles/Generic%20Programming.md) 10 | - [An efficient way to check linked list for palindrome](https://github.com/n0ruSh/blogs/blob/master/articles/An%20efficient%20way%20to%20check%20linked%20list%20for%20palindrome.md) 11 | - [React optimization tips](https://github.com/n0ruSh/blogs/blob/master/articles/React%20optimization%20tips.md) 12 | - [Typescript introduction(Ⅰ)]() 13 | - [Typescript introduction(ⅠI)]() 14 | - [Typescript introduction(ⅠII)]() 15 | - [Controlled and uncontrolled component design pattern in React](https://github.com/n0ruSh/blogs/blob/master/articles/Controlled%20and%20uncontrolled%20component%20design%20pattern%20in%20React.md) 16 | - [An utility to group items in order](https://github.com/n0ruSh/blogs/blob/master/articles/An%20Utility%20to%20group%20items%20in%20order.md) 17 | - [Centering in CSS](https://github.com/n0ruSh/blogs/blob/master/articles/Centering%20in%20CSS.md) 18 | - [Notes from airbnb](https://github.com/n0ruSh/blogs/blob/master/articles/Notes%20from%20airbnb.md) 19 | - [Typescript introduction(ⅠV)]() 20 | - [Implement setInterval with setTimeout](https://github.com/n0ruSh/blogs/blob/master/articles/Implement%20setInterval%20with%20setTimeout.md) 21 | - [Function sleep in JavaScript](https://github.com/n0ruSh/blogs/blob/master/articles/Function%20sleep%20in%20JavaScript.md) 22 | - [Polishing CSS through building a compound input](https://github.com/n0ruSh/blogs/blob/master/articles/Polishing%20CSS%20through%20building%20a%20compound%20input.md) 23 | - [Introduction to ssh](https://github.com/n0ruSh/blogs/blob/master/articles/Introduction%20to%20ssh.md) 24 | - [How href attribute of the anchor element gets me stuck](https://github.com/n0ruSh/blogs/blob/master/articles/How%20href%20attribute%20of%20the%20anchor%20element%20gets%20me%20stuck.md) 25 | - [Transition, transform and animation](https://github.com/n0ruSh/blogs/blob/master/articles/Transition%2C%20transform%20and%20animation.md) 26 | - [CSS variables](https://github.com/n0ruSh/blogs/blob/master/articles/CSS%20variables.md) 27 | - [Get the width of an element](https://github.com/n0ruSh/blogs/blob/master/articles/Get%20the%20width%20of%20an%20element.md) 28 | - [Path related APIs](https://github.com/n0ruSh/blogs/blob/master/articles/Path%20related%20APIs.md) 29 | - [Spread props trap in JSX](https://github.com/n0ruSh/blogs/blob/master/articles/Spread%20props%20trap%20in%20JSX.md) 30 | - [Understanding react key](https://github.com/n0ruSh/blogs/blob/master/articles/Understanding%20react%20key.md) 31 | - [All you need to know about line breaks](https://github.com/n0ruSh/blogs/blob/master/articles/All%20you%20need%20to%20know%20about%20line%20breaks.md) 32 | - [When and why would I want to use SFC declaration](https://github.com/n0ruSh/blogs/blob/master/articles/When%20and%20why%20would%20I%20want%20to%20use%20SFC%20declaration.md) 33 | - [React Hooks - The Ins and Outs](https://github.com/n0ruSh/blogs/blob/master/articles/React%20Hooks%20-%20The%20Ins%20and%20Outs.md) 34 | - [Deep dive into URL](https://github.com/n0ruSh/blogs/blob/master/articles/Deep%20dive%20into%20URL.md) 35 | - [Sticky footer](https://github.com/n0ruSh/blogs/blob/master/articles/Deep%20dive%20into%20URL.md) 36 | - [Encode URI](https://github.com/n0ruSh/blogs/blob/master/articles/Encode%20URI.md) 37 | - [All you need to know about white space in html, css and DOM](https://github.com/n0ruSh/blogs/blob/master/articles/All%20you%20need%20to%20know%20about%20white%20space%20in%20html%2C%20css%20and%20DOM.md) 38 | - [Why third-party cookies are NOT sent where you think they should](https://github.com/n0ruSh/blogs/blob/master/articles/Why%20third-party%20cookies%20are%20NOT%20sent%20where%20you%20think%20they%20should.md) 39 | - [Deep dive into SyntheticEvent in React](https://github.com/n0ruSh/blogs/blob/master/articles/Deep%20dive%20into%20SyntheticEvent%20in%20React.md) 40 | - [Two Css properties you may NOT know](https://github.com/n0ruSh/blogs/blob/master/articles/Two%20Css%20properties%20you%20may%20NOT%20know.md) 41 | - [Mixed Content](https://github.com/n0ruSh/blogs/blob/master/articles/Mixed%20content.md) 42 | - [All you need to know about pixel](https://github.com/n0ruSh/blogs/blob/master/articles/All%20you%20need%20to%20know%20about%20pixel.md) 43 | - [CSS position fixed is NOT always relative to viewport](https://github.com/n0ruSh/blogs/blob/master/articles/CSS%20position%20fixed%20is%20NOT%20always%20relative%20to%20viewport.md) 44 | - [Switch statement](https://github.com/n0ruSh/blogs/blob/master/articles/Switch%20statement.md) 45 | - [How to format code in browser](https://github.com/n0ruSh/blogs/blob/master/articles/How%20to%20format%20code%20in%20browser.md) 46 | -------------------------------------------------------------------------------- /articles/2023-learnings/index.mdx: -------------------------------------------------------------------------------- 1 | # Books 2 | 3 | 1. [JavaScript Essentials](https://www.amazon.com/JavaScript-Essentials-Smashing-eBook-13-ebook/dp/B006P1BL9M) 4 | 5 | Includes basics of JavaScript, only recommended if you are new to JavaScript. Some patterns are outdated due to the advancement of JavaScript and its ecosystem. 6 | 7 | # Courses 8 | -------------------------------------------------------------------------------- /articles/All you need to know about line breaks.md: -------------------------------------------------------------------------------- 1 | ## What’s `downwards arrow with corner leftwards (↵)` 2 | 3 | It’s a [unicode code character](http://www.fileformat.info/info/unicode/char/21B5/index.htm) with code point `8629`. 4 | 5 | ```javascript 6 | let s = "↵"; 7 | let codePont = s.charCodeAt(0); // 8629 8 | let hex = codePoint.toString(16); // 21b5 9 | let hexFormat = "\u21b5"; // '↵' 10 | ``` 11 | 12 | > The character is a human-friendly representation of a newline in the console, like \n is in JavaScript 13 | 14 | ```javascript 15 | let arr = ["a\nb", "a↵b"]; 16 | console.log(arr); 17 | /* 18 | ["a↵b", “a↵b”] // note they look the same on console 19 | */ 20 | 21 | console.log(arr[0]); 22 | /* 23 | "a 24 | b" // note it’s indeed a line break 25 | */ 26 | 27 | console.log(arr[1]); 28 | /* 29 | "c↵d" // note it’s indeed an arrow 30 | */ 31 | 32 | console.log(arr[0] === arr[1]); // false 33 | ``` 34 | 35 | `How the developer tools express a new line in debugging output and how they treat the same character in source code are two different things` 36 | 37 | ## How to preserve line break in html 38 | 39 | > Whitespace in html is compressed into a single space by default 40 | 41 | ```javascript 42 | document.write("Hello\nWorld"); 43 | // You will see 'Hello World' on the page 44 | ``` 45 | 46 | There are two straightforward solutions to make ‘\n’ work in html 47 | 48 | ### Convert \n to `br` tag 49 | 50 | [
: The Line Break element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br) 51 | 52 | ```javascript 53 | document.write("Hello\nWorld".replace(/\n/g, "
")); 54 | ``` 55 | 56 | ### `pre` tag 57 | 58 | The [HTML pre tag](https://www.w3schools.com/tags/tag_pre.asp) defines reformatted text. 59 | 60 | ### css `white-space` 61 | 62 | The [white-space](https://developer.mozilla.org/en-US/docs/Web/CSS/white-space) CSS proper sets how white space inside an element is handled. Possible values are: 63 | 64 | - normal: Sequences of whitespace will collapse into a single whitespace. Text will wrap when necessary. 65 | - nowrap: Sequences of whitespace will collapse into a single whitespace. Text will never wrap to the next line. The text continues on the same line until a br tag is encountered. 66 | - pre: Whitespace is preserved by the browser. Text will only wrap on line breaks. Acts like the pre tag in HTML. 67 | - pre-wrap: Whitespace is preserved by the browser. Text will wrap when necessary, and on line breaks. 68 | 69 | ## Notice 70 | 71 | - If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe 72 | -------------------------------------------------------------------------------- /articles/All you need to know about pixel.md: -------------------------------------------------------------------------------- 1 | # Device/Physical Pixel 2 | 3 | Physical pixels on the device screen, of which there are a fixed amount on any device. 4 | 5 | ``` 6 | iMac (Retina 4K, 21.5-inch, 2017) 7 | 8 | Physical Resolution: 4096 x 2304 9 | ``` 10 | 11 | # Logical pixel 12 | 13 | Information that specifies the color at a particular location on a grid. This type of pixel has no inherent physical size. 14 | 15 | ``` 16 | window.screen.height 17 | window.screen.width 18 | ``` 19 | 20 | ``` 21 | iMac (Retina 4K, 21.5-inch, 2017) 22 | 23 | Default Mode: Logical Resolution is 2048 * 1152 24 | More Space Mode: Logical Resolution is 2560 * 1440 25 | ``` 26 | 27 | # CSS pixel 28 | 29 | An abstraction layer created specifically for web developers to be used in CSS and JS (E.g. `border: 1px solid black` in CSS). It is independent of the device, used to measure pixels logically. Every CSS declaration and nearly every JS property works with CSS pixels, so in practice you'lll never use device/logical pixels. The only exception is `screen.width/height`. 30 | 31 | # Viewport 32 | 33 | ## Layout viewport 34 | 35 | The viewport relative to which the CSS layout is calculated, and which contains the layout. 36 | 37 | ## Visual viewport 38 | 39 | The area of the site the user is currently seeing. The user can manipulate the visual viewport by zooming out or in, without affecting the layout viewport. 40 | 41 | ## Ideal viewport 42 | 43 | The size of the layout viewport that is ideal for the device. 44 | 45 | ```html 46 | 47 | ``` 48 | 49 | tell the browser to make the layout viewport to match the ideal viewport. 50 | 51 | # Zooming 52 | 53 | > screen.width / window.innerWidth 54 | 55 | Zooming is the processing of enlarging CSS pixels. The more user zooms in, the more device/logical pixels are covered by one CSS pixel. One CSS pixel usually equals to one logical pixel without page zoom. Setting the zoom level to 200% will make one CSS pixel to equal to 4 physical pixels. 56 | 57 | # Device Pixel Ratio (DPR) 58 | 59 | Also referred to as CSS pixel ratio, is the ratio of the number of device pixels to the `ideal viewport size`. E.g. 60 | 61 | | Device | Physical Resolution | Device Pixel Ratio | Logical Resolution | 62 | | ----------------- | ------------------- | ------------------ | ------------------ | 63 | | iPhone 6 | 750 × 1334 | 2 | 375 × 667 | 64 | | iPhone X | 1125 × 2436 | 3 | 375 × 812 | 65 | | iPad Pro | 2048 × 2736 | 2 | 1024 × 1368 | 66 | | Samsung Galaxy S4 | 1080 × 1920 | 3 | 360 × 640 | 67 | 68 | ``` 69 | window.devicePixelRatio 70 | ``` 71 | 72 | # Pixels per inch (PPI) - for physical resolution 73 | 74 | Pixel density (also referred to as "screen density" or "display density") measures the density of device pixels in a given physical area. 75 | 76 | E.g. iPhone X (5.8 英寸) with 1125 × 2436px in physical resolution 77 | 78 | PPI = Math.sqrt(Math.pow(1125,2) + Math.pow(2436, 2)) / 5.8 = 462ppi 79 | 80 | # Webview 81 | 82 | A webview is an OS's browsering interface for native apps. E.g. A Twitter client may call on the platform's webview to show a webpage when the user clicks on a link in their feed. In general, webviews are seperate programs that use many low-level components (such as rendering engines) of the default browser, but may differ in other respects. 83 | 84 | # 1x vs 2x vs 3x 85 | 86 | In order for images to look their very best on high resolution screens, it's necessary to provide different image versions for different devicePixelRatios 87 | 88 | Device Pixel Ratio Indicates that: On this device, an tag with a CSS width of 250 pixels, will look best when the source image is.. 89 | 90 | 1 1 device pixel = 1 CSS pixel 250 pixels wide 91 | 2 2 device pixels = 1 CSS pixel 500 pixels wide 92 | 3 3 device pixels = 1 CSS pixel 750 pixels wide 93 | 94 | Things to note: 95 | 96 | The pixel dimensions listed in image editors, file directories, and other places are a measurement of logical pixels. 97 | For higher resolution screens and larger displays you'll need images with larger dimensions. Merely enlarging smaller images defeats the purpose of serving multiple image versions. The browser would have done this anyway if a high resolution image was not provided. 98 | 99 | ## Notice 100 | 101 | - If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe 102 | -------------------------------------------------------------------------------- /articles/All you need to know about white space in html, css and DOM.md: -------------------------------------------------------------------------------- 1 | # What is white space 2 | 3 | > Whitespace is any string of text composed only of spaces, tabs or line breaks (to be precise, CRLF sequences, carriage returns or line feeds). 4 | 5 | ## Whitespace in html 6 | 7 | Whitespaces between words are collapsed into a single character, and whitespace at the start and end of elements and outside elements is ignored. 8 | 9 | > This is so that whitespace characters don't impact the layout of your page. Creating space around and inside elements is the job of CSS. 10 | 11 | ## Whitespace in css 12 | 13 | [white-space property](https://developer.mozilla.org/en-US/docs/Web/CSS/white-space) 14 | 15 | | | New lines | Spaces and tabs | Text wrapping | End-of-line spaces | 16 | |-----------|-----------|-------------------|-----------------|---------------------| 17 | | normal | Collapse | Collapse | Wrap | Remove | 18 | | nowrap | Collapse | Collapse | No wrap | Remove | 19 | | pre | Preserve | Preserve | No wrap | Preserve | 20 | | pre-wrap | Preserve | Preserve | Wrap | Hang | 21 | 22 | 23 | ## References 24 | 25 | * [mdn](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace) 26 | 27 | ## Notice 28 | 29 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 30 | -------------------------------------------------------------------------------- /articles/An Utility to group items in order.md: -------------------------------------------------------------------------------- 1 | # Toggle 2 | 3 | ## Toggle with only two choices 4 | 5 | E.g. toggle a switch whose status can only be `on` and `off`. In the case, we can use `boolean` 6 | 7 | ```javascript 8 | let status = true; 9 | function toggle() { 10 | status = !status; 11 | } 12 | 13 | for(let i = 0; i < 10; i++) { 14 | toggle(); 15 | console.log(status ? 'on' : 'off'); 16 | } 17 | ``` 18 | 19 | 20 | ## Toggle with more than two choices 21 | 22 | E.g. toggle traffic lights. In the case, we can use a counter and increment the counter at each toggle and use `remainder` to get the active light. 23 | 24 | ```javascript 25 | 26 | let counter = 0; 27 | function toggle() { 28 | counter++ 29 | } 30 | 31 | function getActiveLight() { 32 | let remainder = counter % 3; 33 | switch (remainder) { 34 | case 0: 35 | return 'red'; 36 | case 1: 37 | return 'yellow'; 38 | case 2: 39 | return 'green'; 40 | } 41 | } 42 | 43 | for(let i = 0; i < 10; i++) { 44 | toggle(); 45 | console.log(getActiveLight()); 46 | } 47 | ``` 48 | 49 | 50 | # Grouping 51 | 52 | The idea of grouping a list of items into subgroups is similar to the `toggle` example above where we make use of `quotient` and `remainder` 53 | 54 | ## Z Ordering 55 | 56 | > left-to-right, top-to-bottom 57 | 58 | E.g. 59 | 60 | [1,2,3,4,5,6,7] with 3 groups will result in: 61 | 62 | ``` 63 | [[1, 2, 3], [4, 5, 6], [7]] 64 | ``` 65 | 66 | i.e. 67 | 68 | ``` 69 | 1 2 3 70 | 4 5 6 71 | 7 72 | ``` 73 | 74 | ### Implementation 75 | 76 | #### Background 77 | 78 | * An array `arr` 79 | * number of groups/colums `numOfColumns` 80 | 81 | #### Pattern 82 | 83 | ```javascript 84 | A B C D 85 | E F G H 86 | I 87 | ``` 88 | 89 | Given `numOfColumns`, the item with index `idx` should be placed on rowIndex `Math.floor(index / numOfColumns)` and columnIndex `idx % numOfColumns`. For the example, `G` has index `6` and `numOfColumns` is 4. Therefore, 90 | 91 | * rowIndex of `G` would be `Math.floor(idx / numOfColumns) = Math.floor(6 / 4) = 1 ` 92 | * columnIndex of `G` would be `idx % numOfColumns = 6 % 4 = 2` 93 | 94 | => `G` would be on the third column of the second row. (Note: index starts from zero) 95 | 96 | 97 | #### Code 98 | 99 | ```javascript 100 | 101 | const result = []; 102 | arr.forEach((it, index) => { 103 | let rowIndex, columnIndex; 104 | rowIndex = Math.floor(index / numOfColumns); 105 | columnIndex = index % numOfColumns; 106 | result[rowIndex] = result[rowIndex] || []; 107 | result[rowIndex][columnIndex] = it; 108 | }); 109 | ``` 110 | 111 | ## N Ordering 112 | 113 | > top-to-bottom -> left-to-right 114 | 115 | E.g. 116 | 117 | [1,2,3,4,5,6,7] with 3 groups will result in: 118 | 119 | ``` 120 | [[1, 4, 7], [2, 5], [3, 6]] 121 | ``` 122 | 123 | i.e. 124 | 125 | ``` 126 | 1 4 7 127 | 2 5 128 | 3 6 129 | ``` 130 | 131 | ### Implementation 132 | 133 | #### Background 134 | 135 | * An array `arr` 136 | * number of groups/colums `numOfColumns` 137 | 138 | #### Thoughts 139 | 140 | `Z` ordering is a bit counter-intuitive. However, it still follow a pattern similar to `N` ordering. The difference lays on 141 | 142 | * The concept of row and column is the reverse of `N` ordering 143 | * How to deal with `quotient` and `remainder` 144 | 145 | #### Pattern 146 | 147 | ```javascript 148 | A C E G 149 | B D F H 150 | ``` 151 | 152 | Given `numOfColumns`, the number of rows `numOfRows` that we need to have to hold the items would be `Math.ceil(arr.length / numOfColumns)`. The item with index `idx` should be placed on rowIndex `idx % numOfRows` and columnIndex `Math.floor(idx / numOfRows)`. For the example, `G` has index `6` and `numOfColumns` is 4. Therefore, 153 | 154 | * `numOfRows` would be `Math.ceil(arr.length / numOfColumns) = Math.ceil(8 / 4) = 2` 155 | * rowIndex of `G` would be `idx % numOfRows = 6 % 2 = 0` 156 | * columnIndex of `G` would be `Math.floor(idx / numOfRows) = Math.floor(6 / 2) = 3` 157 | 158 | => `G` would be on the fourth column of the first row. (Note: index starts from zero) 159 | 160 | #### Code 161 | 162 | ```javascript 163 | let result = []; 164 | const numOfRows = Math.ceil(arr.length / numOfColumns); 165 | arr.forEach((it, index) => { 166 | let rowIndex, columnIndex; 167 | rowIndex = index % numOfRows; 168 | columnIndex = Math.floor(index / numOfRows); 169 | result[rowIndex] = result[rowIndex] || []; 170 | result[rowIndex][columnIndex] = it; 171 | }); 172 | ``` 173 | 174 | # Source Code 175 | 176 | [Codepen](https://codepen.io/n0rush/pen/jRmbxX) 177 | [github](https://github.com/n0ruSh/to-grid) 178 | [npm](https://www.npmjs.com/package/to-grid) 179 | 180 | # Notice 181 | 182 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 183 | -------------------------------------------------------------------------------- /articles/An efficient way to check linked list for palindrome.md: -------------------------------------------------------------------------------- 1 | ## Background 2 | 3 | > A palindrome is a word, phrase, number or sequence of words that reads the same backwards as forwards. 4 | 5 | E.g. `aba` is a palindrome while `abc` is not. 6 | 7 | ## Q1. how to check whether a string is a palindrome 8 | 9 | `String` in javascript works almost the same as `Array`. It wouldn't be trival to check for palindrome with the help of build-in `Array` utility support. 10 | 11 | * With iteration 12 | 13 | Idea: one pointer from the beginning and another pointer from the end, move both pointers towards the center and compare the values along the way 14 | 15 | ```javascript 16 | function isPalindrome(str) { 17 | const len = str.length; 18 | for(let i = 0; i < len / 2; i++) { 19 | if (str[i] !== str[len - i - 1]) { 20 | return false; 21 | } 22 | } 23 | return true; 24 | } 25 | 26 | console.log(isPalindrome('a')); // true 27 | console.log(isPalindrome('ab')); // false 28 | console.log(isPalindrome('aba')); // true 29 | console.log(isPalindrome('abcba')); // true 30 | console.log(isPalindrome('abcda')); // false 31 | console.log(isPalindrome('abccba')); // true 32 | ``` 33 | 34 | * With recursion 35 | 36 | Similar to the solution above but implemented in recursive way. 37 | 38 | ```javascript 39 | function isPalindromeRecursive(str) { 40 | const len = str.length; 41 | if (len === 0 || len === 1) { 42 | return true; 43 | } 44 | return str[0] === str[len - 1] && isPalindromeRecursive(str.slice(1, len - 1)) 45 | } 46 | 47 | 48 | console.log(isPalindromeRecursive('a')); // true 49 | console.log(isPalindromeRecursive('ab')); // false 50 | console.log(isPalindromeRecursive('aba')); // true 51 | console.log(isPalindromeRecursive('abcba')); // true 52 | console.log(isPalindromeRecursive('abcda')); // false 53 | console.log(isPalindromeRecursive('abccba')); // true 54 | ``` 55 | 56 | ## What if the string is in a singly linked list format ? 57 | 58 | > A linked list consists of nodes where each node contains a data field and a reference(link) to the next node in the list 59 | 60 | ![linkedlist](https://user-images.githubusercontent.com/7504237/59962052-cf37c780-9512-11e9-9b69-49287c8a20d6.png) 61 | 62 | With singly linked list, only the `head` node of the linked list is available and the only way to visit a specific node is by traversing from `head` with `next` pointer up to the node. The main point for checking palindrome is to find the node in the center. Basically this can be achieved by having two pointers go from `head`, one moves two steps forward and the other one step forward until the faster one reaches the end. Also, we build a previous link as the slower pointer moves towards the center. E.g. 63 | 64 | 65 | list: A -> B -> C -> null/end 66 | 67 | Step 1. Both pointers at `A` 68 | Step 2. Slower pointer moves to `B`, build previous link (i.e. B.prev = A). Faster pointer moves to `B` then to `C` 69 | Step 3. Faster pointer is at then end (C.next == null). 70 | Step 4. Center pointer is at B (i.e. where slow pointer is). `NOTE: where center is depends on the parity of the number of nodes` 71 | Step 5. Move slower pointer forward and center pointer backend, check the equality of the value. If value is not the same, not a palindrome. 72 | Step 6. Slow pointer successfully reaches the end => is a palindrome. 73 | 74 | ```javascript 75 | class LinkedListNode { 76 | constructor(char, next) { 77 | this.value = char; 78 | this.next = next; 79 | } 80 | } 81 | 82 | class LinkedList { 83 | constructor(chars) { 84 | const len = chars.length; 85 | let currentNode = null; 86 | for (let i = len - 1; i >= 0; i--) { 87 | let node = new LinkedListNode(chars[i], currentNode); 88 | currentNode = node; 89 | } 90 | this.head = currentNode; 91 | } 92 | 93 | isPalindrome() { 94 | let center, slow, quick; 95 | center = slow = quick = this.head; 96 | if (slow.next == null) { 97 | return true; 98 | } 99 | while(quick.next != null) { 100 | slow = slow.next; 101 | slow.prev = center; 102 | center = slow 103 | quick = quick.next 104 | if (quick.next == null) { 105 | // even number 106 | center = slow.prev; 107 | } else { 108 | quick = quick.next; 109 | } 110 | } 111 | do { 112 | if(slow.value !== center.value) { 113 | return false; 114 | } 115 | slow = slow.next; 116 | center = center.prev; 117 | } while(slow != null) 118 | return true; 119 | } 120 | } 121 | 122 | console.log(new LinkedList(['a']).isPalindrome()); // true 123 | console.log(new LinkedList(['a', 'b']).isPalindrome()); // false 124 | console.log(new LinkedList(['a', 'b', 'a']).isPalindrome()); // true 125 | console.log(new LinkedList(['a', 'b', 'c', 'b', 'a']).isPalindrome()); // true 126 | console.log(new LinkedList(['a', 'b', 'c', 'd', 'a']).isPalindrome()); // false 127 | console.log(new LinkedList(['a','b','c', 'c', 'b', 'a']).isPalindrome()); // true 128 | 129 | ``` 130 | 131 | ## Resource 132 | 133 | [Code Sample](https://codepen.io/n0rush/pen/gQyJwK) 134 | 135 | ## Notice 136 | 137 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 138 | -------------------------------------------------------------------------------- /articles/CSS position fixed is NOT always relative to viewport.md: -------------------------------------------------------------------------------- 1 | # `fixed` position 2 | 3 | For a long time I have been believing that for an element with `position: fixed`, it will be positioned relative to the viewport. However, it's not always the case, as outlined in the docs: 4 | 5 | > It is positioned relative to the initial containing block established by the viewport, except when one of its ancestors has a transform, perspective, or filter property set to something other than none 6 | 7 | Simply put, if any of the closest ancestors has `transform`, `perspective` or `filter` property set, then the closest ancestors become the [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block) 8 | 9 | [Codesandbox](https://codesandbox.io/s/position-fiexed-r7idi) 10 | 11 | ## At what case can it be useful 12 | 13 | When you use a third-party plugin/component with the usage of fixed position but you want it to be positioned relative to your specified container rather than the viewport. Given that you have no control over the third-party source code, wrapping the third-party plugin/component with a container (e.g. `div`) with `transform: translateX(0)` (or whatsoever that won't affect display) would simply make you happy. 14 | 15 | 16 | ## Notice 17 | 18 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/CSS variables.md: -------------------------------------------------------------------------------- 1 | # CSS custom properties/variables 2 | 3 | > Custom properties (sometimes referred to as CSS variables) are entities defined by CSS authors that contain specific values to be reused throughout a document. 4 | 5 | Variables names are prefixed with `--`, like `--example-name`. Variables can be used in other declarations using the `var()` function, which allows you to define multiple fallback values when the given variable is not yet defined. [E.g.](https://codesandbox.io/s/css-variables-ypvdl) 6 | 7 | ```html 8 |

hell paragraph

9 |
hello div
10 | ``` 11 | 12 | ```css 13 | :root { 14 | --main-color: red; 15 | } 16 | 17 | p { 18 | color: var(--main-color); 19 | border: var(--not-present, 1px solid blue); 20 | padding: 10px; 21 | } 22 | 23 | div { 24 | margin-top: 10px; 25 | border: 1px solid var(--main-color); 26 | } 27 | ``` 28 | 29 | # Why should you care? 30 | 31 | ## Variables are already available in CSS preprocessor like [Sass](https://sass-lang.com/) and [Less](http://lesscss.org/). 32 | 33 | E.g. in Sass 34 | 35 | ```sass 36 | $main-color: red; 37 | 38 | p { 39 | color: $main-color; 40 | } 41 | 42 | div { 43 | border: 1px solid $main-color; 44 | } 45 | ``` 46 | 47 | will be converted to 48 | 49 | ```css 50 | p { 51 | color: red; 52 | } 53 | 54 | div { 55 | border: 1px solid red; 56 | } 57 | ``` 58 | 59 | However, CSS preprocessor variables suffer from a major drawback. The variables are static as they are compiled into CSS at compile time, making it impossible to change at run time. 60 | 61 | ## How CSS Variables helps 62 | 63 | > CSS variables are subject to the cascade and inherit their value from their parent. 64 | 65 | [E.g.](https://codepen.io/n0rush/pen/mZoqvq) 66 | 67 | ```html 68 |

hello

69 | ``` 70 | 71 | ```css 72 | :root { 73 | --gutter: 10px; 74 | --main-color: red; 75 | } 76 | 77 | * { 78 | box-sizing: border-box; 79 | } 80 | 81 | .box { 82 | --main-color: blue; 83 | padding: var(--gutter); 84 | width: 100px; 85 | height: 100px; 86 | text-align: center; 87 | border: 1px solid black; 88 | background-color: teal; 89 | background-clip: content-box; 90 | color: var(--main-color); 91 | } 92 | 93 | p { 94 | height: 100%; 95 | display: flex; 96 | align-items: center; 97 | justify-content: center; 98 | margin: 0; 99 | } 100 | 101 | @media (min-width: 300px) { 102 | :root { 103 | --gutter: 20px; 104 | } 105 | }} 106 | ``` 107 | 108 | ## Notice 109 | 110 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/Centering in CSS.md: -------------------------------------------------------------------------------- 1 | > Centering in CSS is should NOT make you frustrated. 2 | 3 | # Horizontal 4 | 5 | ## [text-align](https://developer.mozilla.org/en-US/docs/Web/CSS/text-align) 6 | 7 | > text-align property can be inherited 8 | 9 | ### Use Cases: 10 | 11 | * Inline or inline-block element(s) 12 | 13 | ### [Demo](https://codepen.io/n0rush/pen/bJKoNV) 14 | 15 | ```html 16 |
17 | This text is centered. 18 |
19 | 20 | 25 | ``` 26 | 27 | ```css 28 | header, nav { 29 | text-align: center 30 | } 31 | ``` 32 | 33 | ### [Inheritance Demo](https://codepen.io/n0rush/pen/ZZRXGJ) 34 | 35 | ```html 36 | 37 |
38 |
39 |

Hello to my demo

40 |
41 |
42 | ``` 43 | 44 | ```css 45 | .container { 46 | text-align: center; 47 | } 48 | ``` 49 | 50 | > `p` is not centered, it's a block element and takes the full width. It's just the content/text that is centered. 51 | 52 | ## [margin](https://developer.mozilla.org/en-US/docs/Web/CSS/margin) 53 | 54 | ### Use Cases: 55 | 56 | * Block element 57 | * Width is set 58 | 59 | 60 | ### [Demo](https://codepen.io/n0rush/pen/MRXEKv) 61 | ```html 62 |
63 |

Hello to my demo

64 |
65 | ``` 66 | 67 | ```css 68 | .container p { 69 | width: 200px; 70 | margin: 0 auto; 71 | } 72 | ``` 73 | 74 | ## [position](https://developer.mozilla.org/en-US/docs/Web/CSS/position) 75 | 76 | ### [Demo](https://codepen.io/n0rush/pen/VNdMRY) 77 | 78 | ```html 79 |
80 |
I'm the content, I want to be horizontally centered. I'm the content, I want to be horizontally centered
81 |
82 | ``` 83 | 84 | ```css 85 | .container { 86 | border: 1px solid red; 87 | position: relative; 88 | height: 300px 89 | } 90 | 91 | div { 92 | position: absolute; 93 | left: 50%; 94 | transform: translateX(-50%); 95 | } 96 | ``` 97 | 98 | ## [flex](https://developer.mozilla.org/en-US/docs/Web/CSS/flex) 99 | 100 | ### [Demo](https://codepen.io/n0rush/pen/xezXOJ) 101 | 102 | ```html 103 |
104 |
This is a paragraph. I want to be horizontally centered
105 |
This is a paragraph. I want to be horizontally centered
106 |
This is a paragraph. I want to be horizontally centered
107 |
108 | ``` 109 | 110 | ```css 111 | .container { 112 | display: flex; 113 | justify-content: center; 114 | } 115 | 116 | .container div { 117 | border: 1px solid red; 118 | margin-right: 10px; 119 | } 120 | ``` 121 | 122 | # Vertical 123 | 124 | ## [line-height](https://developer.mozilla.org/en-US/docs/Web/CSS/line-height) 125 | 126 | ### Use Cases: 127 | 128 | * Single line 129 | * Inline or inline block 130 | 131 | ### [Demo](https://codepen.io/n0rush/pen/jRKGpG) 132 | 133 | ```html 134 |
135 |
This is a paragraph. I want to be vertically centered
136 |
137 | ``` 138 | 139 | ```css 140 | div { 141 | border: 1px solid red; 142 | height: 40px; 143 | line-height: 40px; 144 | } 145 | ``` 146 | 147 | ## [position](https://developer.mozilla.org/en-US/docs/Web/CSS/position) 148 | 149 | ### [Demo](https://codepen.io/n0rush/pen/KYeXxy) 150 | 151 | ```html 152 |
153 |
I'm the content, I want to be vertically centered. I'm the content, I want to be vertically centered
154 |
155 | ``` 156 | 157 | ```css 158 | .container { 159 | width: 150px; 160 | position: relative; 161 | height: 300px; 162 | border: 1px solid red; 163 | } 164 | 165 | div { 166 | position: absolute; 167 | top: 50%; 168 | transform: translateY(-50%); 169 | } 170 | ``` 171 | 172 | ## [flex](https://developer.mozilla.org/en-US/docs/Web/CSS/flex) 173 | 174 | ### [Demo](https://codepen.io/n0rush/pen/ROJLEW) 175 | 176 | ```html 177 |
178 |
I'm the content, I want to be vertically centered. I'm the content, I want to be vertically centered
179 |
180 | ``` 181 | 182 | ```css 183 | .container { 184 | width: 150px; 185 | display: flex; 186 | height: 300px; 187 | border: 1px solid red; 188 | align-items: center; 189 | } 190 | ``` 191 | 192 | # Both 193 | 194 | > Combination of the above horizontal and vertical centering solutions 195 | 196 | # Conclusion 197 | 198 | As long as you don't have browser compatibility consideration, try to take `display: flex` as your first choice. It's almost the silver bullet in centering. 199 | 200 | ## Notice 201 | 202 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/Checking the existence of a variable.md: -------------------------------------------------------------------------------- 1 | # undefined 2 | 3 | I used to use `myVar == null` to check the existence of a variable. This expression can detect the case where the variable is `null` or `undefined`. 4 | 5 | There are two cases where a variable can result in `undefined`: 6 | 7 | * The variable is NOT defined. 8 | * The variable is defined but NOT initialized. E.g. 9 | 10 | ```javascript 11 | let a; 12 | ``` 13 | 14 | # Problem 15 | 16 | If a variable is referenced but NOT defined yet in the context, a `Uncaught ReferenceError` will be thrown. 17 | 18 | ``` 19 | // Uncaught ReferenceError: notCreatedVariable is not defined 20 | console.log(notCreatedVariable == null); 21 | ``` 22 | 23 | # Solution 24 | 25 | Use `typeof` 26 | 27 | ``` 28 | console.log(typeof notCreatedVariable === 'undefined'); 29 | ``` 30 | 31 | ## Notice 32 | 33 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/Controlled and uncontrolled component design pattern in React.md: -------------------------------------------------------------------------------- 1 | ## The Uncontrolled 2 | 3 | > An `Uncontrolled` Component is one that stores and maintains its own state internally. 4 | 5 | * A [`ref`](https://reactjs.org/docs/refs-and-the-dom.html) is used to find its current value when you need it. 6 | * [React](https://reactjs.org/docs/uncontrolled-components.html) doesn't recommend this pattern but it's useful when developers only care about the final state rather than the 7 | intermediate state of the component. The following is an [example](https://codepen.io/n0rush/pen/RORrRa) of a `Switch` implemented in `uncontrolled` way. 8 | 9 | ```javascript 10 | class Switch extends React.Component { 11 | constructor(props) { 12 | super(props); 13 | // maintain its own state 14 | this.state = { 15 | checked: false 16 | }; 17 | } 18 | 19 | // expose state data for parent component to access 20 | get value() { 21 | return this.state.checked; 22 | } 23 | 24 | toggle = () => { 25 | this.setState((prevState) => { 26 | return { 27 | checked: !prevState.checked 28 | } 29 | }) 30 | } 31 | 32 | render() { 33 | // check status is maintained in own state 34 | const { checked } = this.state; 35 | let classNames = ['switch']; 36 | 37 | if (checked) { 38 | classNames = [...classNames, 'checked'] 39 | } 40 | return ( 41 |
42 |
44 | ); 45 | } 46 | } 47 | 48 | class Wrapper extends React.Component { 49 | 50 | ref = React.createRef(); 51 | 52 | getValue = () => { 53 | alert(this.ref.current.value); 54 | } 55 | 56 | render() { 57 | return 58 | 59 | 60 | 61 | } 62 | } 63 | 64 | ReactDOM.render( 65 | , 66 | document.getElementById('root') 67 | ); 68 | ``` 69 | 70 | 71 | ## The Controlled 72 | 73 | > A Controlled Component takes its current value through props and notifies changes through callbacks. A parent component "controls" it by handling the callback and managing its own state and passing the new values as props to the controlled component. 74 | 75 | The following is an [example](https://codepen.io/n0rush/pen/jRreVJ) of a `Switch` implemented in controlled way. 76 | 77 | ```javascript 78 | class Switch extends React.Component { 79 | constructor(props) { 80 | super(props); 81 | } 82 | 83 | toggle = () => { 84 | // callback passed in from parent 85 | const { checked, onChange } = this.props; 86 | onChange(!checked) 87 | } 88 | 89 | render() { 90 | // check status is maintained by props from parent component 91 | const { checked } = this.props; 92 | let classNames = ['switch']; 93 | 94 | if (checked) { 95 | classNames = [...classNames, 'checked'] 96 | } 97 | return ( 98 |
99 |
101 | ); 102 | } 103 | } 104 | 105 | class Wrapper extends React.Component { 106 | 107 | state = { 108 | checked: false 109 | } 110 | 111 | getValue = () => { 112 | alert(this.state.checked); 113 | } 114 | 115 | // when check status changes, maintain it in parent state 116 | onChange = (checked) => { 117 | this.setState({ 118 | checked 119 | }) 120 | } 121 | 122 | render() { 123 | return 124 | 125 | 126 | 127 | } 128 | } 129 | 130 | ReactDOM.render( 131 | , 132 | document.getElementById('root') 133 | ); 134 | ``` 135 | 136 | 137 | ## The Mixed 138 | 139 | A `Mixed` Component allows for usage in either `Uncontrolled` or `Controlled` way. It accepts its initial value as a prop and puts it in state. It then reacts to props change through [Component Lifecycle](https://reactjs.org/docs/react-component.html) to sync state update to date with props. 140 | 141 | [Switch Example in Mixed mode](https://codepen.io/n0rush/pen/NmrOao) 142 | 143 | ```javascript 144 | 145 | class Switch extends React.Component { 146 | constructor(props) { 147 | super(props); 148 | // maintain check status in own state 149 | this.state = { 150 | checked: props.checked || false 151 | }; 152 | } 153 | 154 | get value() { 155 | return this.state.checked; 156 | } 157 | 158 | static getDerivedStateFromProps(nextProps, currentState) { 159 | // react to props change and sync state accordingly 160 | // only in controlled mode 161 | if(nextProps.hasOwnProperty('checked') && (nextProps.checked != currentState.checked)) { 162 | return { 163 | checked: nextProps.checked 164 | } 165 | } 166 | return null; 167 | } 168 | 169 | toggle = () => { 170 | const { onChange, checked } = this.props; 171 | const { checked: checkedInState } = this.state 172 | if (!this.props.hasOwnProperty('checked')) { 173 | // no checked prop, uncontrolled 174 | this.setState({ 175 | checked: !checkedInState 176 | }) 177 | } 178 | onChange && onChange(!checkedInState) 179 | } 180 | 181 | render() { 182 | const { checked } = this.state; 183 | let classNames = ['switch']; 184 | 185 | if (checked) { 186 | classNames = [...classNames, 'checked'] 187 | } 188 | return ( 189 |
190 |
192 | ); 193 | } 194 | } 195 | 196 | class Wrapper extends React.Component { 197 | 198 | ref = React.createRef(); 199 | 200 | state = { 201 | controlledChecked: false, 202 | } 203 | 204 | controlledOnChange = (checked) => { 205 | this.setState({ 206 | controlledChecked: checked; 207 | }) 208 | } 209 | 210 | getUncontrolledValue = () => { 211 | alert(this.ref.current.value); 212 | } 213 | 214 | getControlledValue = () => { 215 | alert(this.state.controlledCheck); 216 | } 217 | 218 | render() { 219 | return 220 |
221 | Uncontrolled: 222 | 225 |
226 |
227 |
228 | Controlled: 229 | 232 |
233 |
234 |
235 | } 236 | } 237 | 238 | ReactDOM.render( 239 | , 240 | document.getElementById('root') 241 | ); 242 | ``` 243 | 244 | ## Notice 245 | 246 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/DRY principle.md: -------------------------------------------------------------------------------- 1 | ## Background 2 | 3 | We want to have a function which checks whether an item in JS object representation is a commondity. An item is a commondity if it satisfies one of : 4 | 5 | * HAS a price attribute 6 | * HAS a barcode attribute 7 | 8 | The following tests should pass: 9 | 10 | ```javascript 11 | isCommondity({price: 1}); //true 12 | isCommondity({barcode: 'abc'}); //true 13 | isCommondity({a: 'a'}); //false 14 | ``` 15 | 16 | ## Round one 17 | 18 | It seems to be quite straightfordword to come out with the following solution: 19 | 20 | ```javascript 21 | function isCommondityV1(comd) { 22 | return !!comd.price || !!comd.barcode 23 | } 24 | ``` 25 | 26 | The implementation is wrong under some circumstances. `!!obj.prop` is checking the [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) of the `prop` property of `obj`, instead of checking the existence of the `prop` property. E.g. 27 | 28 | ``` 29 | isCommondityV1({price: 0}); // false 30 | ``` 31 | while we expect `isCommondityV1` to return true since `{price: 0}` does have the `price` property. 32 | 33 | ## Round two 34 | 35 | [hasOwnProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) 36 | 37 | ```javascript 38 | function isCommondityV2(comd) { 39 | return comd.hasOwnProperty('price') || comd.hasOwnProperty('barcode') 40 | } 41 | 42 | isCommondityV2({price: 0}); //true 43 | ``` 44 | 45 | The solution works perfectly in line with the functionality requirement. However, it does suffer from the two problems: 46 | 47 | * Flexibility: if we add more checkings later on, we would have to update the implementation body. E.g. when the attribute set extends to be `['price', 'barcode', 'a', 'b']`. 48 | 49 | ```javascript 50 | function isCommondityV21(comd) { 51 | return comd.hasOwnProperty('price') || 52 | comd.hasOwnProperty('barcode') || 53 | comd.hasOwnProperty('a') || 54 | comd.hasOwnProperty('b'); 55 | } 56 | ``` 57 | 58 | * DRY: We're repeating the operation (i.e `comd.hasOwnProperty`). We can be better off by seperating the variables and encapsulate the constants. 59 | 60 | 61 | ## Round three 62 | 63 | ```javascript 64 | function isCommondityV3(comd) { 65 | return ['price', 'barcode'].some(Object.prototype.hasOwnProperty, comd); 66 | } 67 | ``` 68 | 69 | With this implementation, we can easily deal with later change by updating the variables. 70 | 71 | ```javascript 72 | function isCommondityV31(comd) { 73 | return ['price', 'barcode', 'a', 'b'].some(Object.prototype.hasOwnProperty, comd); 74 | } 75 | ``` 76 | 77 | Or we can even go one more step further by encapsulating the variables into `config` file. Futher requirement change will result in only changes in `config` file without touching the implementation body :-) 78 | 79 | ```javascript 80 | //config.js 81 | export COMMONDITY_PROPS = ['price', 'barcode']; 82 | ``` 83 | 84 | ```javascript 85 | //commondity.js 86 | import { COMMONDITY_PROPS } from 'config.js' 87 | 88 | function isCommondity(comd) { 89 | return COMMONDITY_PROPS.some(Object.prototype.hasOwnProperty, comd); 90 | } 91 | ``` 92 | 93 | ## Notice 94 | 95 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 96 | -------------------------------------------------------------------------------- /articles/Deep dive into SyntheticEvent in React.md: -------------------------------------------------------------------------------- 1 | # Deep dive into SyntheticEvent in React 2 | 3 | > `SyntheticEvent` is a wrapper that forms part of React’s Event System 4 | 5 | ## Why it's useful 6 | 7 | - Cross-browser: It wraps the brower's native event through `nativeEvent` attribute and provices a uniform api and consistent behavior on top level 8 | - Better performance: Events are [delegated](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events) to document through bubbling. [Note Event Pooling is removed in React 17](https://reactjs.org/blog/2020/08/10/react-v17-rc.html#no-event-pooling) 9 | 10 | ## What it's like 11 | 12 | Every `SyntheticEvent` object has the following attributes 13 | 14 | ```bash 15 | boolean bubbles 16 | boolean cancelable 17 | DOMEventTarget currentTarget // the element that the handler is registered 18 | boolean defaultPrevented 19 | number eventPhase 20 | boolean isTrusted 21 | DOMEvent nativeEvent 22 | void preventDefault() 23 | boolean isDefaultPrevented() 24 | void stopPropagation() 25 | boolean isPropagationStopped() 26 | void persist() 27 | DOMEventTarget target // the element that the handler is triggered 28 | number timeStamp 29 | string type 30 | ``` 31 | 32 | ## When native and synthetic events are mixed 33 | 34 | Synthetic events are delegated to `document` node. Therefore native events are triggered first and the events bubble up to doucment, afther which the synthetic events are triggerd. 35 | 36 | ```javascript 37 | import React, { useEffect } from "react"; 38 | 39 | export default function App() { 40 | useEffect(() => { 41 | document.addEventListener('click', e => { 42 | // @4 stop parent native propogation 43 | // e.stopPropagation(); 44 | console.log('[document] native dom event triggered'); 45 | }); 46 | const parentDiv = document.getElementById('parent'); 47 | parentDiv.addEventListener('click', e => { 48 | // @1 stop parent native propogation 49 | // e.stopPropagation(); 50 | console.log('[parent div] native dom event triggered'); 51 | }); 52 | }, []); 53 | 54 | const onParentClick = e => { 55 | // @2 stop parent propogation 56 | // e.stopPropagation(); 57 | console.log('[parent div] synthetic event triggered'); 58 | }; 59 | 60 | const onChildClick = e => { 61 | // @3 stop child propogation 62 | // e.stopPropagation(); 63 | console.log('[child button] synthetic event triggered'); 64 | }; 65 | 66 | return
67 | 68 |
; 69 | } 70 | ``` 71 | 72 | The output of clicking `child` button with various setting will be: 73 | 74 | #### No stopPropogation - Output: 75 | 76 | ```bash 77 | [parent div] native dom event triggered 78 | [child button] synthetic event triggered 79 | [parent div] synthetic event triggered 80 | [document] native dom event triggered 81 | ``` 82 | 83 | native dom event is registered later as it's called after component is mounted. 84 | 85 | 86 | #### stopPropogation on parent's native node (i.e. @1) - Output: 87 | 88 | ```bash 89 | [parent div] native dom event triggered 90 | ``` 91 | 92 | No bubbling up to document as if there is no click event happening on document => synthetic events are NOT triggered 93 | 94 | #### stopPropogation on parent's synthetic event (i.e. @2) - Output: 95 | 96 | ```bash 97 | [parent div] native dom event triggered 98 | [child button] synthetic event triggered 99 | [parent div] synthetic event triggered 100 | [document] native dom event triggered 101 | ``` 102 | 103 | #### stopPropogation on child's synthetic event (i.e. @3) - Output: 104 | 105 | ```bash 106 | [parent div] native dom event triggered 107 | [child button] synthetic event triggered 108 | [document] native dom event triggered 109 | ``` 110 | 111 | #### stopPropogation on document's native event (i.e. @4) - Output: 112 | 113 | ```javascript 114 | [parent div] native dom event triggered 115 | [child button] synthetic event triggered 116 | [parent div] synthetic event triggered 117 | [document] native dom event triggered 118 | ``` 119 | 120 | ## Key points 121 | 122 | - Listen on `document` if you want to receive events after all React handlers. Listen anywhere else in order to receive before React handlers 123 | - React event handlers will always execute after native capture handlers 124 | 125 | 126 | [Code](https://codesandbox.io/s/synthetic-event-ruv3f?file=/src/App.js) 127 | 128 | ## Notice 129 | 130 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 131 | -------------------------------------------------------------------------------- /articles/Deep dive into URL.md: -------------------------------------------------------------------------------- 1 | > The **`URL`** interface is used to parse, construct, normalize, and encode [URLs](https://developer.mozilla.org/en-US/docs/Glossary/URL). It works by providing properties which allow you to easily read and modify the components of a URL 2 | 3 | ## Basic 4 | 5 | ### [SearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URL/searchParams) 6 | 7 | > The **`searchParams`** readonly property of the [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) interface returns a [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) object allowing access to the [`GET`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) decoded query arguments contained in the URL. 8 | 9 | ```javascript 10 | const url = new URL('https://www.google.com:443/index.html?a=1&b=2#hash'); 11 | /** 12 | * URL { 13 | * href: "https://www.google.com:443/index.html?a=1&b=2#hash", 14 | * protocol: "https:", 15 | * username: "", 16 | * password: "", 17 | * hash: "#hash, 18 | * origin: "https://www.google.com:443", // with port, with protocl 19 | * host: "www.google.com:443", // with port, without protocol 20 | * hostname: "www.google.com", // without port, without protocl 21 | * search: "?a=1&b=2" 22 | * searchParams: {} // URLSeachParams 23 | * } 24 | */ 25 | url.searchParams.get('a'); // '1' 26 | ``` 27 | ## createObjectURL 28 | 29 | > The **`URL.createObjectURL()`** static method creates a [`DOMString`](https://developer.mozilla.org/en-US/docs/Web/API/DOMString) containing a URL representing the object given in the parameter. The URL lifetime is tied to the [`document`](https://developer.mozilla.org/en-US/docs/Web/API/Document) in the window on which it was created. The new object URL represents the specified [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) object or [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) object. 30 | 31 | ### Blob 32 | 33 | > The **`Blob`** object represents a blob, which is a file-like object of immutable, raw data; they can be read as text or binary data, or converted into a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) so its methods can be used for processing the data. 34 | 35 | ```javascript 36 | const obj = {hello: 'world'}; 37 | const blob = new Blob([JSON.stringify(obj, null, 2)], {type : 'application/json'}); 38 | /** 39 | * Blob { 40 | * size: 22, // JSON.stringify(obj, null, 2).length === 22 41 | * type: 'application/json' 42 | * } 43 | */ 44 | // get usable url: "blob:https://developer.mozilla.org/ff415f41-2b1b-4e47-bc1c-37112e0ff4b2" 45 | const linkUrl = URL.createObjectURL(blob); 46 | ``` 47 | ### File 48 | 49 | > A `File` object is a specific kind of a [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob), and can be used in any context that a Blob can. In particular, [`FileReader`](https://developer.mozilla.org/en-US/docs/Web/API/FileReader), [`URL.createObjectURL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL), [`createImageBitmap()`](https://developer.mozilla.org/en-US/docs/Web/API/ImageBitmapFactories/createImageBitmap), and [`XMLHttpRequest.send()`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#send()) accept both `Blob`s and `File`s 50 | 51 | ```javascript 52 | const file = new File(["hello", "world"], "test.txt", { 53 | type: "text/plain", 54 | }); 55 | 56 | /** 57 | * File { 58 | * name: "test.txt", 59 | * lastModified: 1594093337229, 60 | * lastModifiedDate: Tue Jul 07 2020 11:42:17 GMT+0800 (China Standard Time), 61 | * webkitRelativePath: "", 62 | * size: 10, 63 | * type: "text/plain", 64 | * __proto__: File 65 | *} 66 | */ 67 | ``` 68 | #### Preveiw uploaded images 69 | ```javascript 70 | export default function App() { 71 | function onChange(e) { 72 | const file = e.target.files[0]; // get file object 73 | const div = document.getElementById('img'); 74 | const img = document.createElement("img"); 75 | const url = URL.createObjectURL(file); // convert to url 76 | img.src = url; 77 | div.appendChild(img); 78 | } 79 | 80 | return ( 81 |
82 | 83 |
84 |
85 |
86 | ); 87 | } 88 | ``` 89 | [demo](https://codesandbox.io/s/preview-file-330tx?file=/src/App.js) 90 | 91 | ### FileReader 92 | 93 | > The **`FileReader`** object lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user's computer, using [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) or [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) objects to specify the file or data to read 94 | 95 | ```javascript 96 | export default function App() { 97 | function onChange(e) { 98 | const file = e.target.files[0]; 99 | const div = document.getElementById("img"); 100 | const img = document.createElement("img"); 101 | const reader = new FileReader(); 102 | reader.onload = function(e) { 103 | img.src = e.target.result; // base64 data of the uploaded image 104 | div.appendChild(img); 105 | }; 106 | reader.readAsDataURL(file); 107 | } 108 | 109 | return ( 110 |
111 | 112 |
113 |
114 | ); 115 | } 116 | 117 | ``` 118 | [demo](https://codesandbox.io/s/base64-22zjd?file=/src/App.js:414-446) 119 | 120 | ## Real life case 121 | 122 | I get an [error](https://github.com/mozilla/pdf.js/issues/7612) reporting `Setting up fake worker` when using [pdfjs-dist](https://www.npmjs.com/package/pdfjs-dist). The users are supposed to set up worker as following: 123 | 124 | ```javascript 125 | import pdflib from 'pdfjs-dist'; 126 | /** 127 | * set up with the address pointing to the worker file 128 | * the address is a String and not dealt by bunlding tool like webpack 129 | * the relative path doesnt work for the bundle after packaging 130 | */ 131 | pdflib.PDFJS.workerSrc = './node_modules/pdfjs-dist/build/pdf.worker.entry.js'; 132 | ``` 133 | ### solution 134 | ```javascript 135 | import pdfjsLib from 'pdfjs-dist'; 136 | import pdfjsWorker from 'raw-loader!pdfjs-dist/build/pdf.worker.min'; 137 | 138 | const pdfjsWorkerBlob = new Blob([pdfjsWorker]); 139 | const pdfjsWorkerBlobURL = URL.createObjectURL(pdfjsWorkerBlob); 140 | 141 | pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorkerBlobURL; 142 | ``` 143 | -------------------------------------------------------------------------------- /articles/Encode URI.md: -------------------------------------------------------------------------------- 1 | # Encode URI 2 | 3 | # **encodeURIComponent** 4 | > The `**encodeURIComponent()**` function encodes a [URI](https://developer.mozilla.org/en-US/docs/Glossary/URI) by replacing each instance of certain characters by one, two, three, or four escape sequences representing the [UTF-8](https://developer.mozilla.org/en-US/docs/Glossary/UTF-8) encoding of the character (will only be four escape sequences for characters composed of two "surrogate" characters). 5 | 6 | It's commonly applied to a quring string parameter with a link type. E.g. https://www.mysite.com/login?redirectUrl=https%3A%2F%2Fwww.other.com%3Fa%3Db%26c%3Dd&mysiteParam1=a&mysiteParam2=b 7 | 8 | In the example link, `mysite.com` has three query params { `redirectUrl,` `mysiteParam1`, `mysiteParam2` } which are separated by `&`. The original `redirectUrl` link is https://www.other.com?a=b&c=d. The `=` and `&` in the original redirect link, if not encoded, will confuse with the query params of `mysite.com`. 9 | 10 | # Versus encodeURI 11 | 12 | `encodeURIComponent(value)` is mainly used to encode queryString parameter values, and it encodes every applicable character in `value`. `encodeURI` ignores protocol prefix (`http://`) and domain name. If you're encoding a string to put in a URL component (a query string parameter), you should use **encodeURIComponent**, and if you're encoding an existing URL, use **encodeURI**. It's simple 13 | 14 | ```javascript 15 | const link = 'http://www.google.com/a b/c_d?a=b&c=d' 16 | 17 | // http://www.google.com/a%20b/c_d?a=b&c=d 18 | encodeURI(link); 19 | // http%3A%2F%2Fwww.google.com%2Fa%20b%2Fc_d%3Fa%3Db%26c%3Dd 20 | encodeURIComponent(link); 21 | ``` 22 | 23 | ## Notice 24 | 25 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 26 | -------------------------------------------------------------------------------- /articles/Function sleep in JavaScript.md: -------------------------------------------------------------------------------- 1 | > In Java, [Thread.sleep](https://docs.oracle.com/javase/tutorial/essential/concurrency/sleep.html) causes the current thread to suspend execution for a specified period. 2 | 3 | This is an efficient means of making processor time available to the other threads of an application or other applications that might be running on a computer system. However, JavaScript does NOT have such a native implementation. Thanks to [async function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function), we can emulate the behavior: 4 | 5 | ```javascript 6 | 7 | async function sleep(interval) { 8 | return new Promise(resolve => { 9 | setTimeout(resolve, interval); 10 | }) 11 | } 12 | ``` 13 | 14 | ## Use case: print number 1 to 5 per second in consecutive way. 15 | 16 | ### [Async & Await implementation](https://codepen.io/n0rush/pen/XLropN) 17 | 18 | ```javascript 19 | async function one2FiveInAsync() { 20 | for(let i = 1; i <= 5; i++) { 21 | console.log(i); 22 | await sleep(1000) 23 | } 24 | } 25 | 26 | one2FiveInAsync(); 27 | ``` 28 | 29 | ### [Promise implementation](https://codepen.io/n0rush/pen/orvJWp) 30 | 31 | ```javascript 32 | 33 | function one2FiveInPromise() { 34 | function logAndSleep(i) { 35 | console.log(i); 36 | if (i === 5) { 37 | return; 38 | } 39 | return sleep(1000).then(() => logAndSleep(i + 1)); 40 | } 41 | 42 | logAndSleep(1); 43 | } 44 | 45 | one2FiveInPromise(); 46 | ``` 47 | 48 | ## Notice 49 | 50 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/Generic Programming.md: -------------------------------------------------------------------------------- 1 | > Generic programming centers around the idea of abstracting from concrete, efficient algorithms to obtain generic algorithms that can be combined with different data representations to produce a wide variety of useful software. 2 | 3 | > — Musser, David R.; Stepanov, Alexander A., Generic Programming 4 | 5 | ## Goal 6 | 7 | * To make the algorithm/method/solution work generically by encapsulating the details about data structures and operations. 8 | * To allow developers to focus on the solution instead of differentiating data structures in the implementation. 9 | 10 | ## Don't talk, show the example 11 | 12 | ### Calculate the sum of an array 13 | 14 | ```javascript 15 | function sum(arr) { 16 | let total = 0; 17 | for(let i = 0; i < arr.length; i++) { 18 | total += arr[i]; 19 | } 20 | return total; 21 | } 22 | 23 | console.log(sum([1,2,3])); //6 24 | ``` 25 | 26 | We iterate through the array and add up each element to get the sum of the array. But what if we want a different operation, such as finding the max number: 27 | 28 | ### Find the max number in an array 29 | 30 | ```javascript 31 | function max(arr) { 32 | let result = arr[0]; 33 | for(let i = 1; i < arr.length; i++) { 34 | if(arr[i] > result) { 35 | result = arr[i] 36 | } 37 | } 38 | return max; 39 | } 40 | 41 | console.log(max([1,2,3])); //3 42 | ``` 43 | 44 | ### How can we do better? 45 | 46 | There might be many cases in which we need to go through the array and do something with the elements in the array. It should be a good option to encapsulate the operation and leave it to the caller to determine what to do with each element. That's what [reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) is designed for. Let's implement our own **_reduce**. 47 | 48 | ```javascript 49 | 50 | function _reduce(op, initialValue) { 51 | return (arr) => { 52 | let result; 53 | let index = 0; 54 | if(initialValue != null) { 55 | result = initialValue; 56 | } else { 57 | result = arr[0]; 58 | index = 1; 59 | } 60 | for(let i = index; i < arr.length; i++) { 61 | result = op(result, arr[i]) 62 | } 63 | return result; 64 | } 65 | } 66 | ``` 67 | 68 | So now we can rewrite **sum** and **max** as: 69 | 70 | ```javascript 71 | let sumWithReduce = _reduce((sum, item) => sum + item) 72 | let maxWithReduce = _reduce((max, item) => max > item ? max : item) 73 | 74 | console.log(sumWithReduce([1,2,3])); //6 75 | console.log(maxWithReduce([1,2,3])); //3 76 | ``` 77 | 78 | ### How can we do even better? 79 | 80 | In the **_reduce** implementation above, we have abstracted away the operation detail and only keep the looping logic in the implementation. However, the implementation still relies on **for** and **array index** so it only works for **arrays**. We make it more generic with [Iterators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators). 81 | 82 | ```javascript 83 | function _genericReduce(op, initialValue) { 84 | return (iterables) => { 85 | let inited, result; 86 | if(initialValue != null) { 87 | result = initialValue; 88 | inited = true; 89 | } 90 | for(let item of iterables) { 91 | if(!inited) { 92 | result = item; 93 | inited = true; 94 | } else { 95 | result = op(result, item) 96 | } 97 | } 98 | return result; 99 | } 100 | } 101 | 102 | let sumWithGenericReduce = _genericReduce((sum, item) => sum + item) 103 | let maxWithGenericReduce = _genericReduce((max, item) => max > item ? max : item) 104 | 105 | console.log(sumWithGenericReduce([1,2,3])); //6 106 | console.log(maxWithGenericReduce([1,2,3])); //3 107 | ``` 108 | 109 | It now works for all data structures that are *iterable*. Here is a reading note I wrote about [iterator](https://github.com/n0ruSh/the-art-of-reading/issues/10) if you are interested in. 110 | 111 | 112 | ```javascript 113 | class Group { 114 | constructor(id) { 115 | this.id = id; 116 | this.members = []; 117 | } 118 | 119 | addMember(person) { 120 | this.members.push(person); 121 | } 122 | 123 | [Symbol.iterator]() { 124 | let index = 0; 125 | return { 126 | next: () => ({ 127 | value: this.members[index++], 128 | done: index > this.members.length 129 | }) 130 | }; 131 | } 132 | } 133 | 134 | class Member { 135 | constructor(name, salary) { 136 | this.name = name; 137 | this.salary = salary; 138 | } 139 | } 140 | 141 | 142 | ## try to find the lowest salary in the group 143 | 144 | let group = new Group('007'); 145 | group.addMember(new Member('mike', 1000)) 146 | group.addMember(new Member('john', 2000)) 147 | group.addMember(new Member('alfred', 3000)) 148 | let lowestSalary = _genericReduce((result, member) => { 149 | if(member.salary < result) { 150 | return member.salary 151 | } 152 | return result; 153 | }, Number.MAX_SAFE_INTEGER)(group) 154 | console.log(lowestSalary); 155 | ``` 156 | 157 | ## Resource 158 | 159 | [Code Sample](https://codepen.io/n0rush/pen/MzmzEJ) 160 | 161 | ## Notice 162 | 163 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /articles/Get the width of an element.md: -------------------------------------------------------------------------------- 1 | > Each html element exposes some properties to facilitate fetching element dimensions 2 | 3 | ## [clientWidth](https://developer.mozilla.org/en-US/docs/Web/API/Element/clientWidth) 4 | 5 | > `clientWidth` is the inner width (ie. the space inside an element including padding but excluding borders and scrollbar) 6 | 7 | * `clientWidth` does NOT work with `inline` elements. It is zero for `inline` elements. 8 | * This property will round the value to an integer. 9 | 10 | ## [offsetWidth](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetWidth) 11 | 12 | > `offsetWidth` is the outer width (ie. the space occupied by the element, including padding and borders) 13 | 14 | ## [getBoundingClientRect](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect) 15 | 16 | * This property will round the value to an integer. 17 | 18 | > `getBoundingClientRect` method returns the size of an element and its position relative to the viewport. 19 | 20 | * `getBoundingClientRect` only returns the dimensions, but also the position which can be pretty useful. E.g. [Implement position:fixed with position:absolute](https://github.com/n0ruSh/the-art-of-reading/blob/master/articles/Fixed%20with%20absolute.md). 21 | * This method will return a fractional value. 22 | 23 | 24 | ```html 25 |
26 | ``` 27 | 28 | ```css 29 | .box { 30 | width: 200.5px; 31 | height: 100px; 32 | border: 5px solid red; 33 | padding: 5px; 34 | background-image: linear-gradient(to right, teal, orange) 35 | } 36 | ``` 37 | 38 | ```javascript 39 | const { Fragment } = React; 40 | 41 | function BlockBox(props) { 42 | const divRef = React.createRef(); 43 | const [clientWidth, setClientWidth] = React.useState(0); 44 | const [offsetWidth, setOffsetWidth] = React.useState(0); 45 | const [boundingWidth, setBoundingWidth] = React.useState(0); 46 | React.useEffect(() => { 47 | const { clientWidth: cw, offsetWidth: ow } = divRef.current; 48 | setClientWidth(cw); 49 | setOffsetWidth(ow); 50 | setBoundingWidth(divRef.current.getBoundingClientRect().width); 51 | }, []); 52 | 53 | 54 | function renderInfo() { 55 | return <> 56 |
57 | bounding width: {boundingWidth} 58 |
59 |
60 | client width: {clientWidth} 61 |
62 |
63 | offset width: {offsetWidth} 64 |
65 | 66 | } 67 | 68 | return
69 | { renderInfo() } 70 |
; 71 | } 72 | 73 | function InlineBox(props) { 74 | const spanRef = React.createRef(); 75 | const [clientWidth, setClientWidth] = React.useState(0); 76 | const [offsetWidth, setOffsetWidth] = React.useState(0); 77 | const [boundingWidth, setBoundingWidth] = React.useState(0); 78 | React.useEffect(() => { 79 | const { clientWidth: cw, offsetWidth: ow } = spanRef.current; 80 | setClientWidth(cw); 81 | setOffsetWidth(ow); 82 | setBoundingWidth(spanRef.current.getBoundingClientRect().width); 83 | }, []); 84 | 85 | 86 | function renderInfo() { 87 | return <> 88 | 89 | bounding width: {boundingWidth} 90 | 91 | 92 | client width: {clientWidth} 93 | 94 | 95 | offset width: {offsetWidth} 96 | 97 | 98 | } 99 | 100 | return 101 | { renderInfo() } 102 | ; 103 | } 104 | 105 | ReactDOM.render(
, document.getElementById('app')) 106 | ``` 107 | 108 | [Comparison](https://codepen.io/n0rush/pen/dBEwzO) 109 | 110 | ## Notice 111 | 112 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 113 | -------------------------------------------------------------------------------- /articles/How href attribute of the anchor element gets me stuck.md: -------------------------------------------------------------------------------- 1 | Frontend developers are all familiar with [the anchor element ](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a). I was recently struggling with a bug which took me a while to figure out. The root cause is an unexpected use of `href` attribute. 2 | 3 | > href: Contains a URL or a URL fragment that the hyperlink points to. URLs are not restricted to Web (HTTP)-based documents, but can use any protocol supported by the browser. 4 | 5 | # Use case 6 | 7 | I build a multi-level menus with the following requirements: 8 | 9 | * Only the leaf menu item is an active link 10 | * Clicking other non-leaf menu items should expand the next level 11 | 12 | ## What I did 13 | 14 | It comes in handy for making each menu item with ``. The only difference between leaf menu items and non-leaf menu items is that leaf menu items should have valid `href` which indicates the redirection destination. Therefore, I build a `MenuItem` component. E.g. 15 | 16 | ```javascript 17 | 18 | // Note, the example is in React 19 | function MenuItem({link, title, onClick = () => {}}) { 20 | return {title} 21 | } 22 | ``` 23 | 24 | ## The Problem 25 | 26 | ### Leaf menu items 27 | 28 | Leaf menu items work fine as the source `{ link: '/aaa', title: 'abc'}` would be rendered as ` abc ` 29 | 30 | 31 | ### Non-leaf menu items 32 | 33 | For non-leaf menu items, the source `{ link: '', title: 'abc', onClick: console.log }` would be rendered as ` abc `. Clicking such an anchor link will reload the page. The reason is that the current page is assumed to be the value of `href` attribute if the `href` attribute exists but no value is set for it. i.e. 34 | 35 | ```html 36 | aa 37 | ``` 38 | 39 | is equivalent to 40 | 41 | ```html 42 | aa 43 | ``` 44 | 45 | ## Solution 46 | 47 | ### Sol.1 - don't set href if link is exceptional 48 | 49 | ```javascript 50 | function MenuItem({link, title, onClick = () => {}}) { 51 | if(link) { 52 | return {title} 53 | } 54 | return {title} 55 | } 56 | ``` 57 | 58 | ### Sol.2 - reset link if link is exceptional 59 | 60 | ```javascript 61 | function MenuItem({link, title, onClick = () => {}}) { 62 | if (!link) { 63 | link = '#' 64 | } 65 | return {title} 66 | } 67 | ``` 68 | 69 | # Conclusion 70 | 71 | To conclude, an anchor element with `href` attribute present but no value set for it will make it an active link to the current page. Having this knowledge in mind may save you time when you get into a similar track :-) 72 | 73 | ## Notice 74 | 75 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/How to format code in browser.md: -------------------------------------------------------------------------------- 1 | # Format code 2 | 3 | There are times where we get codes by computation and would like to format them before displaying on the page. 4 | 5 | ## [prettier](https://prettier.io/) 6 | 7 | Prettier comes with cli support which can be used on `node` environment. However, using prettier to format code on browser is possible. More detail can be found [here](https://prettier.io/docs/en/browser.html). E.g. 8 | 9 | ```js 10 | import prettier from "https://unpkg.com/prettier@2.5.1/esm/standalone.mjs"; 11 | import parserBabel from "https://unpkg.com/prettier@2.5.1/esm/parser-babel.mjs"; 12 | import parserHtml from "https://unpkg.com/prettier@2.5.1/esm/parser-html.mjs"; 13 | 14 | console.log( 15 | prettier.format("const html=/* HTML */ `
`", { 16 | parser: "babel", 17 | plugins: [parserBabel, parserHtml], 18 | }) 19 | ); 20 | // Output: const html = /* HTML */ `
`; 21 | ``` 22 | 23 | [Sandbox example](https://codesandbox.io/s/code-format-gqi1h) 24 | 25 | ## [js-beautify](https://github.com/beautify-web/js-beautify) 26 | 27 | ```js 28 | import { js_beautify } from "js-beautify"; 29 | const rawJsCode = ` 30 | function add(a) { 31 | return function(b){ 32 | return a + b; 33 | } 34 | } 35 | 36 | sum(3)(5) 37 | `; 38 | 39 | js_beautify(rawJsCode); 40 | 41 | /* Output: 42 | * function add(a) { 43 | * return function(b) { 44 | * return a + b; 45 | * } 46 | * } 47 | * 48 | * sum(3)(5) 49 | ``` 50 | 51 | [Sandbox example](https://codesandbox.io/s/code-format-gqi1h) 52 | 53 | ## Notice 54 | 55 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/Implement setInterval with setTimeout.md: -------------------------------------------------------------------------------- 1 | ## setInterval vs setTimeout 2 | 3 | * [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout): sets a timer which executes a function or specified piece of code once the timer expires. 4 | * [setInterval](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval): *repeatedly* calls a function or executes a code snippet, with a fixed time delay between each call 5 | 6 | *setInterval* can be implemented as `recurisve` *setTimeout* calls. 7 | 8 | # [setInterval implementation with setTimeout](https://codepen.io/n0rush/pen/RmqObZ) 9 | 10 | with signature 11 | 12 | ```typescript 13 | function _setInterval(fn: Function, delay: number): number; 14 | ``` 15 | 16 | The call stack will be: 17 | 18 | --delay--> fn() --delay--> fn() --delay--> fn() ... 19 | 20 | To delay the execution of `fn`, we could simply use *setTimeout*. Therefore, we create a `wrapper` function which encapsulates the logic which executes the original function. 21 | 22 | ```javascript 23 | function _setInterval(fn, delay) { 24 | // wrap the original function, recursively call the wrapper function with setTimeout 25 | const wrapper = () => { 26 | setTimeout(fn, 0); 27 | return setTimeout(wrapper, delay) 28 | } 29 | 30 | setTimeout(wrapper, delay); 31 | } 32 | 33 | _setInterval(console.log.bind(null, 'hello world'), 1000); 34 | ``` 35 | 36 | ## Notice 37 | 38 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/Introduction to ssh.md: -------------------------------------------------------------------------------- 1 | > ssh (SSH client) is a program for logging into a remote machine and for executing commands on a remote machine. It is intended to provide secure encrypted communications between two untrusted hosts over an insecure network. 2 | 3 | ## Example 4 | 5 | ```bash 6 | - Connect to a remote server: 7 | ssh username@remote_host 8 | 9 | - Connect to a remote server with a specific identity (private key): 10 | ssh -i path/to/key_file username@remote_host 11 | 12 | - Connect to a remote server using a specific port: 13 | ssh username@remote_host -p 2222 14 | 15 | - Run a command on a remote server: 16 | ssh remote_host command -with -flags 17 | 18 | - SSH tunneling: Dynamic port forwarding (SOCKS proxy on localhost:9999): 19 | ssh -D 9999 -C username@remote_host 20 | 21 | - SSH tunneling: Forward a specific port (localhost:9999 to slashdot.org:80) along with disabling pseudo-[t]ty allocation and executio[n] of remote commands: 22 | ssh -L 9999:slashdot.org:80 -N -T username@remote_host 23 | 24 | - SSH jumping: Connect through a jumphost to a remote server (Multiple jump hops may be specified separated by comma characters): 25 | ssh -J username@jump_host username@remote_host 26 | 27 | - Agent forwarding: Forward the authentication information to the remote machine (see `man ssh_config` for available options): 28 | ssh -A username@remote_host 29 | ``` 30 | 31 | ## Public key authentication 32 | 33 | Local system has a cryptographic key pair - public key and private key. The server is configured to recognize the public key by adding it to [~/.ssh/authorized_keys](https://www.ssh.com/ssh/authorized_keys/). Anyone that has the corresponding private key will be granted access to the server. 34 | 35 | ## Client config setting 36 | 37 | Instead of annoyingly typing 38 | 39 | ```ssh 40 | ssh root@11.111.222.333 -p 2333 41 | ``` 42 | 43 | We can actually set the ssh config in `~/.ssh/config` with 44 | 45 | ```ssh 46 | Host remoteServer # host name alias that is easy to memorize 47 | HostName 11.111.222.333 # host ip or host name 48 | User root # user 49 | Port 2333 # port 50 | IdentityFile ~/.ssh/id_rsa # private key location 51 | ``` 52 | 53 | Then we can do the equivalent with easy typing: 54 | 55 | ```ssh 56 | ssh remoteServer 57 | ``` 58 | 59 | -------------------------------------------------------------------------------- /articles/Minimum font size.md: -------------------------------------------------------------------------------- 1 | ## Minimum font size 2 | 3 | Major browsers have a default minimum font size that they support. 4 | 5 | * For Chrome, the minimum font size can be found and configured following the path 6 | 7 | ``` 8 | Settings > Appearance > Customize fonts > Minimum font size 9 | ``` 10 | 11 | 12 | * For Edge, the minimum font size can be found and configured following the path 13 | 14 | ``` 15 | Settings > Appearance > Fonts > Customize fonts > Minimum font size 16 | ``` 17 | 18 | * To compute the minimum font size the browser supports, with script: 19 | 20 | ```javascript 21 | const div = document.createElement('div'); 22 | div.style.fontSize = '3px'; 23 | div.style.lineHeight = '1'; 24 | div.innerHTML = 'a'; 25 | document.body.appendChild(div); 26 | const minimumFontSize = div.clientHeight; 27 | ``` 28 | 29 | ## How to display font size smaller than the minimum one 30 | 31 | Say the minimum font size is `12px` and we want the text to be `9px`, We can use [`transform`](https://developer.mozilla.org/en-US/docs/Web/CSS/transform) 32 | 33 | ```css 34 | .text { 35 | font-size: 12px; 36 | transform: scale(0.0.75); // 9 / 12 = 0.75 37 | transform-origin: left; // depending on how the text is aligned 38 | } 39 | ``` 40 | 41 | ## Notice 42 | 43 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe 44 | -------------------------------------------------------------------------------- /articles/Mixed content.md: -------------------------------------------------------------------------------- 1 | # [Mixed Content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content) 2 | 3 | > An HTTPS page that includes content fetched using cleartext HTTP is called a mixed content page. Pages like this are only partially encrypted, leaving the unencrypted content accessible to sniffers and man-in-the-middle attackers 4 | 5 | There are two types of mixed content: `mixed passive/display content` and `mixed active content` 6 | 7 | ## Mixed passive/display content 8 | 9 | > Mixed passive/display content is content served over HTTP that is included in an HTTPS webpage, but that cannot alter other portions of the webpage 10 | 11 | * \ (src attribute) 12 | * \ (src attribute) 13 | * \ (src attribute) 14 | * \ subresources 15 | 16 | ## Mixed active content 17 | 18 | > Mixed active content is content that has access to all or parts of the Document Object Model of the HTTPS page 19 | 20 | * \ (src attribute) 21 | * \ (href attribute) 22 | * \ (src attribute) 23 | * fetch requests 24 | 25 | ## To avoid mixed content 26 | 27 | * Always use `https` resources - Loading `https` resources on `http` page is valid while the reverse is not. 28 | * Use relative protocol urls - requests the resource from whatever protocol the browser is viewing that current page through 29 | 30 | ## Notice 31 | 32 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 33 | -------------------------------------------------------------------------------- /articles/Nginx Buffer Problem.md: -------------------------------------------------------------------------------- 1 | ## Background 2 | I have been using nginx as [reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy) for my daily development work. After upgrading nginx today, it somehow stops working correctly: a vendor.js file built from webpack has not been served properly. I get the following error from chrome console: 3 | 4 | ```bash 5 | GET http://nginx.example.com/dist/js/vendor.js net::ERR_CONTENT_LENGTH_MISMATCH 6 | ``` 7 | 8 | I take some quick lookarounds and find out: 9 | 10 | * Network tab shows the vendor.js request has a status code 200. 11 | * The response header seems to be fine. However, there is nothing to preview in chrome network tab. 12 | * The Content-Length of the vendor.js matches the real size of the file. 13 | 14 | ```bash 15 | # ls -l : get the size in byte 16 | -rw-r--r-- 1 michaelzheng staff 4844863 Jul 2 17:00 vendor.js 17 | 18 | # ls -lh : get the size in human readable format 19 | -rw-r--r-- 1 michaelzheng staff 4.6M Jul vendor.js 20 | ``` 21 | * Loading the vendor.js directly through browser or `curl` works perfectly. 22 | 23 | ## Debug 24 | 25 | * Find the location of nginx config file(i.e. nginx.conf) 26 | 27 | ```bash 28 | nginx -t 29 | ``` 30 | 31 | * Find the location of error log, which is usually defined in nginx.conf 32 | * Check out the error 33 | 34 | ```bash 35 | # tail -f nginx 36 | 2018/07/02 22:29:27 [crit] 14586#0: *3641 open() "/usr/local/var/run/nginx/proxy_temp/1/08/0000000081" failed 37 | (13: Permission denied) while reading upstream, client: 127.0.0.1, server: nginx.example.com, request: "GET /dist/js/vendor.js HTTP/1.1", 38 | upstream: "http://127.0.0.1:8080/dist/js/vendor.js", host: "nginx.example.com", referrer: "http://nginx.example.com/testabc" 39 | ``` 40 | 41 | Now that we can clearly see it's related to permission problem of the buffer directory `proxy_temp`. 42 | 43 | ## Solutions 44 | 45 | * Solution 1 - Grant permission 46 | 47 | ```bash 48 | chown -R nobody:admin proxy_temp 49 | ``` 50 | 51 | You need to first run the command `ps aux | grep nginx` to find out the owner of the nginx process which is usually `nobody` 52 | 53 | * Solution 2 - Disable buffering with [proxy\_buffering](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering) set to `off` 54 | 55 | * Solution 3 - Change the buffer directory to other directory the nginx process owner has permission to, with [proxy\_temp\_path](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_temp_path) directive. 56 | 57 | ## Notice 58 | 59 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 60 | -------------------------------------------------------------------------------- /articles/Notes from airbnb.md: -------------------------------------------------------------------------------- 1 | # To convert an `iterable` object to an array, use spreads ... instead of Array.from 2 | 3 | ```javascript 4 | const foo = document.querySelectorAll('.foo'); 5 | 6 | // good 7 | const nodes = Array.from(foo); 8 | 9 | // best 10 | const nodes = [...foo]; 11 | ``` 12 | 13 | # Use Array.from for converting an `array-like` object to an array. 14 | 15 | ```javascript 16 | const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }; 17 | 18 | // bad 19 | const arr = Array.prototype.slice.call(arrLike); 20 | 21 | // good 22 | const arr = Array.from(arrLike); 23 | ``` 24 | 25 | # Use Array.from instead of spread ... for mapping over iterables, because it avoids creating an intermediate array. 26 | 27 | ```javascript 28 | // bad 29 | const baz = [...foo].map(bar); 30 | 31 | // good 32 | const baz = Array.from(foo, bar); 33 | ``` 34 | 35 | # Use exponentiation operator ** when calculating exponentiations 36 | 37 | ```javascript 38 | // bad 39 | const binary = Math.pow(2, 10); 40 | 41 | // good 42 | const binary = 2 ** 10; 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /articles/Path related APIs.md: -------------------------------------------------------------------------------- 1 | ``` 2 | +-- myProject 3 | | +-- node_modules 4 | | +-- lodash 5 | | +-- src 6 | | +-- cwd.js 7 | | +-- dirname.js 8 | | +-- require.js 9 | ``` 10 | 11 | switch current working directory into `myProject` 12 | 13 | ```bash 14 | pwd // '/Users/michaelzheng/Desktop/myProject' 15 | ``` 16 | 17 | ## [process.cwd](https://nodejs.org/docs/latest/api/process.html#process_process_cwd) 18 | 19 | > The process.cwd() method returns the current working directory of the node.js process. i.e. the directory from which you invoked the `node` command. 20 | 21 | `relative to the process` 22 | 23 | ```javascript 24 | #!/usr/bin/env node 25 | 26 | // cwd.js 27 | process.cwd(); 28 | // Returns '/Users/michaelzheng/Desktop/myProject' 29 | ``` 30 | 31 | Use case: fetch project config file which is usually set based on project root. e.g. webpack config file. 32 | 33 | ## [__dirname](https://nodejs.org/docs/latest/api/modules.html#modules_dirname) 34 | > returns the directory name of the directory containing the JavaScript source code file. 35 | 36 | `relative to the source code` 37 | 38 | ```javascript 39 | #!/usr/bin/env node 40 | 41 | // dirname.js 42 | __dirname; 43 | // Returns '/Users/michaelzheng/Desktop/myProject/src' 44 | ``` 45 | 46 | Use case: get absolute path of a file 47 | 48 | ## [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options) 49 | > use(es) the internal require() machinery to look up the location of a module, but rather than loading the module, just return(s) the resolved filename. 50 | 51 | `works for module resolution` 52 | 53 | ```javascript 54 | 55 | // require.js 56 | require.resolve('lodash'); 57 | // Returns /Users/michaelzheng/Desktop/myProject/node_modules/lodash/lodash.js 58 | ``` 59 | 60 | Use case: resolve node modules which are not installed on current working directory. 61 | 62 | ## [path.resolve](https://nodejs.org/docs/latest/api/path.html#path_path_resolve_paths) 63 | 64 | > The path.resolve() method resolves a sequence of paths or path segments into an `absolute path`. 65 | 66 | `always returns absolute path` 67 | 68 | ```javascript 69 | 70 | path.resolve('/foo/bar', './baz'); 71 | // Returns: '/foo/bar/baz' 72 | 73 | path.resolve('/foo/bar', '/tmp/file/'); 74 | // Returns: '/tmp/file' 75 | ``` 76 | 77 | ## [path.join](https://nodejs.org/docs/latest/api/path.html#path_path_join_paths) 78 | 79 | > The path.join() method joins all given path segments together using the platform-specific separator as a delimiter, then normalizes the resulting path. 80 | 81 | ```javascript 82 | path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'); 83 | // Returns '/foo/bar/baz/asdf' 84 | 85 | path.join('foo', 'bar', 'baz/asdf', 'quux', '..'); 86 | // Returns 'foo/bar/baz/asdf' 87 | ``` 88 | 89 | ## Notice 90 | 91 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/Polishing CSS through building a compound input.md: -------------------------------------------------------------------------------- 1 | In the post I will list the steps taken to build a compound input component and some interesting CSS observations made through the process. 2 | 3 | ## Round one 4 | 5 | ```html 6 | 7 | 8 | ``` 9 | 10 | ```css 11 | .input { 12 | padding: 5px; 13 | border: 1px solid #d1c9c9; 14 | } 15 | .input:focus { 16 | border-color: #3c5ed9; 17 | } 18 | ``` 19 | 20 | ![outline](https://user-images.githubusercontent.com/7504237/59962072-0d34eb80-9513-11e9-8e9a-ab701884ee67.gif) 21 | 22 | There are two problems with the above solution. 23 | 24 | * A line is drawn around elements to make the input "stand out" when the input is clicked. 25 | 26 | 27 |
Why and How 28 |

29 | The line is [outline](https://developer.mozilla.org/en-US/docs/Web/CSS/outline). It can be removed by setting `outline: none`. 30 |

31 |
32 | 33 | * Currently there are some spaces between the two inputs while I expect them to sit near each other. 34 | 35 |
Why and How 36 |

37 | No margins are applied to the input elements. The space is the character from html code. There are a few solutions we can apply to solve the problem. 38 | 39 | * Remove the character 40 | 41 | E.g. 42 | 43 | ```html 44 | 45 | ``` 46 | 47 | * Apply negative margin to one of the input elements to shift its position 48 | 49 | * Wrap the inputs with a parent container and set font size to 0 on parent. 50 |

51 |
52 | 53 | 54 | We then result in the following solution: 55 | 56 | ```html 57 |
58 | 59 | 60 |
61 | ``` 62 | 63 | ```css 64 | .compound-input { 65 | font-size: 0; 66 | } 67 | 68 | .input { 69 | outline: none; 70 | padding: 5px; 71 | border: 1px solid #d1c9c9; 72 | } 73 | 74 | .input:focus { 75 | border-color: #3c5ed9; 76 | } 77 | ``` 78 | 79 | ## Round two 80 | 81 | ![borders](https://user-images.githubusercontent.com/7504237/59962079-28076000-9513-11e9-9d4a-0e2f4f9d7b4a.png) 82 | 83 | Now that the two inputs are near each other. However, the right border of the left input and the left border of the right input adds up and it looks inharmonic. Setting a negative `margin-right` to the first input to shift it towards the second input should fix the problem, as now the two borders stack. 84 | 85 | ```html 86 |
87 | 88 | 89 |
90 | ``` 91 | 92 | ```css 93 | .compound-input { 94 | font-size: 0; 95 | } 96 | 97 | .input { 98 | outline: none; 99 | padding: 5px; 100 | border: 1px solid #d1c9c9; 101 | } 102 | 103 | .input:focus { 104 | border-color: #3c5ed9; 105 | } 106 | 107 | .input:first-child { 108 | margin-right: -1px; 109 | } 110 | ``` 111 | 112 | ## Round three 113 | 114 | ![border stack](https://user-images.githubusercontent.com/7504237/59962086-381f3f80-9513-11e9-8d55-c140829be087.gif) 115 | 116 | By setting negative `margin-left: -1px` to the first input element, the right-border of first input element is now always missing. Clicking the first input will no longer has a right border highlight effect. The first idea that comes up in mind is to set the [z-index](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index) to the element that has [focus state](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus). To make it work, we also have to set the `position: relative` to both inputs as `z-index` only works for a positioned box (that is, one with any position other than static) 117 | 118 | [Final Solution](https://codepen.io/n0rush/pen/BgWddO) 119 | 120 | ```html 121 |
122 | 123 | 124 |
125 | ``` 126 | 127 | ```css 128 | .compound-input { 129 | font-size: 0; 130 | } 131 | 132 | .input { 133 | outline: none; 134 | padding: 5px; 135 | border: 1px solid #d1c9c9; 136 | position: relative; 137 | } 138 | 139 | .input:focus { 140 | border-color: #3c5ed9; 141 | } 142 | 143 | .protocol { 144 | margin-right: -1px; 145 | } 146 | 147 | .input:first-child:focus { 148 | z-index: 10; 149 | } 150 | ``` 151 | 152 | ## Notice 153 | 154 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe -------------------------------------------------------------------------------- /articles/React Hooks - The Ins and Outs.md: -------------------------------------------------------------------------------- 1 | # React Hooks - The Ins and Outs 2 | 3 | > [Hooks](https://reactjs.org/docs/hooks-intro.html) are functions that let you “hook into” React state and lifecycle features from function components. 4 | 5 | ## Why hooks 6 | 7 | ### How did we reuse stateful logic - [HOC](https://reactjs.org/docs/higher-order-components.html) 8 | 9 | E.g. 10 | 11 | ```javascript 12 | import { withRouter } from "react-router-dom"; 13 | import { Form } from 'antd'; 14 | 15 | function A(props) { 16 | const { 17 | match: { params }, 18 | history, 19 | form 20 | } = props; 21 | 22 | return
test
; 23 | } 24 | 25 | export default withRouter(Form.create()(A)) 26 | ``` 27 | 28 | There are a few drawbacks with HOC. 29 | 30 | - **wrapper hell** of components. HOC actually returns a new component that wraps the original component. 31 | - unclear dependencies 32 | - from the view of Componet **A**, we have no way to clearly know **this.props.form **is from **Form.create** HOC, and **this.props.history** is from **withRouter** HOC 33 | - naming conflicts 34 | - imagine that **Form.create** and **withRouter** both inject a prop with the same name, which can easily happen as they are two seperate HOCs that are properly maintained by two teams 35 | 36 | ### How can we improve with Hooks 37 | 38 | ```javascript 39 | import { useHistory } from "react-router-dom"; 40 | import { Form } from 'antd'; 41 | 42 | export default function A(props) { 43 | const [form] = Form.useForm(); 44 | const history = useHistory(); 45 | 46 | return
test
; 47 | } 48 | ``` 49 | 50 | - no wrapper any more as they are all at the same level 51 | - clear dependencies as they are from separate hooks call: **form** from **useForm** and **history** from **useHistory** 52 | - no naming conflicts as they are from seperate hooks and you can rename hooks return values 53 | 54 | ## Hooks in practice 55 | 56 | To implement a controlled **Input** element with **reset** functionality, we can do it [this way](https://codesandbox.io/s/controlled-input-with-reset-2js6i?file=/src/App.js): 57 | 58 | ```javascript 59 | import React, { useState } from "react"; 60 | import { Input, Button } from 'antd'; 61 | 62 | export default function App() { 63 | const initialValue = 'please enter something'; 64 | const [inputValue, setInputValue] = useState(initialValue); 65 | 66 | return ( 67 |
68 | // note the value is from e.target 69 | setInputValue(e.target.value)}/> 70 | 71 |
72 | ); 73 | } 74 | ``` 75 | 76 | There are a couple of stateful logic that we can abstract and resue. 77 | 78 | - value fetch - e.target.value 79 | - reset logic - reset to initial value 80 | 81 | ### custom hook - [useEventTarget](https://codesandbox.io/s/useeventtarget-z18i3?file=/src/useEventTarget.js) 82 | 83 | ```javascript 84 | import { useState, useCallback } from "react"; 85 | 86 | export default function useEventTarget(initialValue = "") { 87 | const [value, setValue] = useState(initialValue); 88 | const onChange = useCallback(e => setValue(e.target.value), []); // reusable value fetch logic 89 | const reset = useCallback(() => setValue(initialValue), [initialValue]); // resuable reset logic 90 | 91 | return { 92 | value, 93 | onChange, 94 | reset 95 | }; 96 | } 97 | ``` 98 | 99 | The above example can then be [refactored](https://codesandbox.io/s/useeventtarget-z18i3?file=/src/App.js) with custom useEventTarget hook: 100 | 101 | ```javascript 102 | import React from "react"; 103 | import { Input, Button } from "antd"; 104 | import useEventTarget from "./useEventTarget"; 105 | 106 | export default function App() { 107 | const { value, onChange, reset } = useEventTarget("please enter something"); 108 | 109 | return ( 110 |
111 | 112 | 113 |
114 | ); 115 | } 116 | ``` 117 | 118 | ## Mental overhead 119 | 120 | > Hooks is NOT a substitue for class lifecycles though useEffect can simulate what lifecycles do. Hooks facilitates reusing stateful logic but it comes with costs. 121 | 122 | Assume that I need a page that display **count** which is from zero and increment by one per second. 123 | 124 | ```javascript 125 | import React, { useState, useEffect } from 'react'; 126 | 127 | export default function SetInterval() { 128 | const [ count, setCount ] = useState(0); 129 | 130 | useEffect(() => { 131 | const interval = setInterval(() => { 132 | console.log("interval count", count); // count is always 0 133 | setCount(count + 1); 134 | }, 1000); 135 | return () => { 136 | clearInterval(interval) 137 | } 138 | }, []); 139 | 140 | return
normal count is { count }
; 141 | } 142 | ``` 143 | 144 | With [the code above](https://codesandbox.io/s/setinterval-wt720?file=/src/App.js), the count on the page will stay on 1. This is due to the fact that **useEffect** call only runs on mount (as **useEffect** dependency is set to [] on line 14) and the **count** value at that point is 0 (i.e the initial value we set with **useState** call on line 4). Each time the interval function is called, the **count** value will always be 0 as it's a closure variable. Therefore, setCount(count + 1) will be setCount(1). 145 | 146 | ### [Solution 1 - useEffect dependencies](https://codesandbox.io/s/setinterval-with-dependency-36fb5?file=/src/App.js:0-355) 147 | 148 | > Specify the dependencies if your useEffect depends on outer variables 149 | 150 | ```javascript 151 | import React, { useState, useEffect } from "react"; 152 | 153 | export default function SetInterval() { 154 | const [count, setCount] = useState(0); 155 | useEffect(() => { 156 | const interval = setInterval(() => { 157 | console.log('interval count', count); // increment by one per run 158 | setCount(count + 1); 159 | }, 1000); 160 | 161 | return () => { 162 | // each time count value changes (i.e per second, clear the interval and init one with new count value 163 | console.log('interval clear', count); 164 | clearInterval(interval); 165 | } 166 | }, [count]); 167 | 168 | return
dep count is {count}
; 169 | } 170 | ``` 171 | 172 | ### [Solution 2 - use state callback](https://codesandbox.io/s/setinterval-with-prev-vsi1y) 173 | 174 | ```javascript 175 | import React, { useState, useEffect } from "react"; 176 | 177 | export default function SetInterval() { 178 | const [count, setCount] = useState(0); 179 | 180 | useEffect(() => { 181 | const interval = setInterval(() => { 182 | // it doesnt matter what count was, it tells React to increment by one with previous value 183 | setCount(a => a + 1); 184 | }, 1000); 185 | 186 | return () => { 187 | clearInterval(interval); 188 | }; 189 | }, []); 190 | 191 | return
prev normal count is {count}
; 192 | } 193 | ``` 194 | 195 | ### [Solution 3 - useRef](https://codesandbox.io/s/setinterval-with-ref-qldj0?file=/src/App.js) 196 | 197 | ```javascript 198 | import React, { useRef, useState, useEffect } from "react"; 199 | 200 | export default function SetInterval() { 201 | const [count, setCount] = useState(0); 202 | const ref = useRef(); 203 | ref.current = count; 204 | 205 | useEffect(() => { 206 | const interval = setInterval(() => { 207 | console.log("interval count", ref.current); 208 | setCount(ref.current + 1); 209 | }, 1000); 210 | 211 | return () => { 212 | clearInterval(interval); 213 | }; 214 | }, []); 215 | 216 | return
ref count is {count}
; 217 | } 218 | ``` 219 | 220 | ## Notice 221 | 222 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/React optimization tips.md: -------------------------------------------------------------------------------- 1 | In React, the component literally re-renders while its state or props change. While it makes sense as state represents internal data and props represent external data, it's not always the case. There are a few techniques which can prevent the component from unnecessary rendering. Let's go through the examples. 2 | 3 | ## Always re-render 4 | 5 | ```javascript 6 | let count = 0; 7 | 8 | class Button extends React.Component { 9 | render = () => { 10 | const { color } = this.props 11 | count++; 12 | return ( 13 | 14 | Render count: { count } 15 | 16 | 17 | ) 18 | } 19 | } 20 | 21 | class App extends React.Component { 22 | state = { 23 | color: 'red' 24 | } 25 | 26 | showRedButton = () => { 27 | this.setState({ 28 | color: 'red' 29 | }) 30 | } 31 | 32 | showGreenButton = () => { 33 | this.setState({ 34 | color: 'green' 35 | }) 36 | } 37 | 38 | render() { 39 | const { color } = this.state; 40 | const { showRedButton, showGreenButton } = this; 41 | return ( 42 | 43 | 45 | 46 | ) 47 | } 48 | } 49 | 50 | ReactDOM.render(, document.getElementById('root')) 51 | 52 | ``` 53 | 54 | [Online Code Sample](https://codepen.io/n0rush/pen/MZwwZr) 55 | 56 | Each time we click the buttons (either `red` or `green`), the `count` increments, indicating the `Button` component is re-rendering. However, the only prop that the `Button` is dependent on is `color`. If the `color` keeps unchanging, the rendering is unnecessary. 57 | 58 | Goal: If we keep pressing the `red` button, we want the `count` to be unchanged as we dont want to re-render `Button` component since `color` remains the same. 59 | 60 | ## Technique 1: shouldComponentUpdate 61 | 62 | ```javascript 63 | 64 | let count = 0; 65 | 66 | class Button extends React.Component { 67 | shouldComponentUpdate(nextProps) { 68 | if(this.props.color === nextProps.color) { 69 | return false; 70 | } 71 | return true 72 | } 73 | 74 | render = () => { 75 | const { color } = this.props 76 | count++; 77 | return ( 78 | 79 | Render count: { count } 80 | 81 | 82 | ) 83 | } 84 | } 85 | 86 | class App extends React.Component { 87 | state = { 88 | color: 'red' 89 | } 90 | 91 | showRedButton = () => { 92 | this.setState({ 93 | color: 'red' 94 | }) 95 | } 96 | 97 | showGreenButton = () => { 98 | this.setState({ 99 | color: 'green' 100 | }) 101 | } 102 | 103 | render() { 104 | const { color } = this.state; 105 | const { showRedButton, showGreenButton } = this; 106 | return ( 107 | 108 | 110 | 111 | ) 112 | } 113 | } 114 | 115 | ReactDOM.render(, document.getElementById('root')) 116 | ``` 117 | 118 | [ShouldComponentUpdate Code Sample](https://codepen.io/n0rush/pen/KbpdGr) 119 | 120 | ## Technique 2: React.PureComponent 121 | 122 | ### How it works 123 | 124 | ```javascript 125 | let count = 0; 126 | 127 | class Button extends React.PureComponent { 128 | render = () => { 129 | const { color } = this.props 130 | count++; 131 | return ( 132 | 133 | Render count: { count } 134 | 135 | 136 | ) 137 | } 138 | } 139 | 140 | class App extends React.Component { 141 | state = { 142 | color: 'red' 143 | } 144 | 145 | showRedButton = () => { 146 | this.setState({ 147 | color: 'red' 148 | }) 149 | } 150 | 151 | showGreenButton = () => { 152 | this.setState({ 153 | color: 'green' 154 | }) 155 | } 156 | 157 | render() { 158 | const { color } = this.state; 159 | const { showRedButton, showGreenButton } = this; 160 | return ( 161 | 162 | 164 | 165 | ) 166 | } 167 | } 168 | 169 | ReactDOM.render(, document.getElementById('root')) 170 | ``` 171 | 172 | [PureComponent Code Sample](https://codepen.io/n0rush/pen/JwdYVG) 173 | 174 | ### Silver bullet? 175 | 176 | `PureComponent` is basically `Component` with build-in `shouldComponentUpdate` hook. It seems that we should use `PureCompnent` at any cases since it has native rendering optimization? 177 | Not really. `PureComponent` is using shallow equality for props and state comparison. 178 | 179 | ```javascript 180 | 181 | class TodoList extends React.PureComponent { 182 | render() { 183 | const { todos } = this.props; 184 | return (
    185 | { 186 | todos.map((it, index) =>
  • {it}
  • ) 187 | } 188 |
) 189 | } 190 | } 191 | 192 | class App extends React.Component { 193 | inputRef = React.createRef() 194 | 195 | state = { 196 | todos: [] 197 | } 198 | 199 | onAdd = () => { 200 | let { todos } = this.state; 201 | const text = this.inputRef.current.value; 202 | todos.push(text); 203 | this.setState({ 204 | todos 205 | }); 206 | } 207 | 208 | render() { 209 | const { todos } = this.state; 210 | const { onAdd, inputRef } = this 211 | return ( 212 | 213 | 214 | 215 | 216 | 217 | ) 218 | } 219 | } 220 | 221 | ReactDOM.render(, document.getElementById('root')) 222 | 223 | ``` 224 | 225 | [Anti PureComponent](https://codepen.io/n0rush/pen/wRKKZp) 226 | 227 | In the example, `TodoList` will never re-render as `todos` (which is the state from `App` and is passed in as props to `TodoList`) always has the same reference. 228 | 229 | ### How to make it work? 230 | 231 | ```javascript 232 | 233 | class TodoList extends React.PureComponent { 234 | render() { 235 | const { todos } = this.props; 236 | return (
    237 | { 238 | todos.map((it, index) =>
  • {it}
  • ) 239 | } 240 |
) 241 | } 242 | } 243 | 244 | class App extends React.Component { 245 | inputRef = React.createRef() 246 | 247 | state = { 248 | todos: [] 249 | } 250 | 251 | onAdd = () => { 252 | let { todos } = this.state; 253 | const text = this.inputRef.current.value; 254 | this.setState({ 255 | todos: [...todos, text] 256 | }); 257 | } 258 | 259 | render() { 260 | const { todos } = this.state; 261 | const { onAdd, inputRef } = this 262 | return ( 263 | 264 | 265 | 266 | 267 | 268 | ) 269 | } 270 | } 271 | 272 | ReactDOM.render(, document.getElementById('root')) 273 | 274 | ``` 275 | 276 | [PureComponent reference](https://codepen.io/n0rush/pen/wRKMMj) 277 | 278 | It now works as we now make a new `todos` array each time we call the `setState` 279 | 280 | ## React.memo - for functional component 281 | 282 | ### How it works 283 | 284 | ```javascript 285 | let count = 0; 286 | 287 | const Button = React.memo(function(props) { 288 | const { color } = props 289 | count++; 290 | return ( 291 | 292 | Render count: { count } 293 | 294 | 295 | ) 296 | }) 297 | 298 | class App extends React.Component { 299 | state = { 300 | color: 'red' 301 | } 302 | 303 | showRedButton = () => { 304 | this.setState({ 305 | color: 'red' 306 | }) 307 | } 308 | 309 | showGreenButton = () => { 310 | this.setState({ 311 | color: 'green' 312 | }) 313 | } 314 | 315 | render() { 316 | const { color } = this.state; 317 | const { showRedButton, showGreenButton } = this; 318 | return ( 319 | 320 | 322 | 323 | ) 324 | } 325 | } 326 | 327 | ReactDOM.render(, document.getElementById('root')) 328 | ``` 329 | 330 | ## Notice 331 | 332 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 333 | -------------------------------------------------------------------------------- /articles/Security risk for opening new tabs or windows.md: -------------------------------------------------------------------------------- 1 | ## Background 2 | 3 | Today eslint reports an error when I introduce [eslint-plugin-react](https://www.npmjs.com/package/eslint-plugin-react) 4 | 5 | ``` 6 | error Using target="_blank" without rel="noopener noreferrer" is a security risk: see https://mathiasbynens.github.io/rel-noopener react/jsx-no-target-blank 7 | ``` 8 | 9 | ## Why 10 | 11 | Opening a new tab/window, either by hyperlinks (i.e tag with target attribute set to `_blank`) or programmatically calling [window.open](https://developer.mozilla.org/en-US/docs/Web/API/Window/open), will grant the newly-opened tab/window access back to the originating tab/window via [window.opener](https://developer.mozilla.org/en-US/docs/Web/API/Window/opener). Therefore, the newly opened tab/window can then change the `window.opener.location` to redirect to the phishing page in the background. Or execute some JavaScript on the opener-page on your behalf. 12 | 13 | ## How to fix 14 | 15 | * [hyperlink types](https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types) 16 | 17 | Add `rel="noopenner"` to outgoing links. E.g. 18 | 19 | ``` 20 | 21 | ``` 22 | 23 | * Specify `noopener` in [window features](https://developer.mozilla.org/en-US/docs/Web/API/Window/open#Window_features) 24 | 25 | ```javascript 26 | window.open('https://abc.com', 'security', 'noopener'); 27 | ``` 28 | 29 | * Reset `opener` property 30 | 31 | Note: this technique is subject to [Same Origin Policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) 32 | 33 | ```javascript 34 | let nw = window.open('https://abc.com', 'security'); 35 | nw.opener = null; 36 | ``` 37 | 38 | ## Reference 39 | 40 | * [About rel=noopener](https://mathiasbynens.github.io/rel-noopener/) 41 | * [Target="_blank" - the most underestimated vulnerability ever](https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/) 42 | 43 | ## Notice 44 | 45 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 46 | -------------------------------------------------------------------------------- /articles/Spread props trap in JSX.md: -------------------------------------------------------------------------------- 1 | # Spread props trap in JSX 2 | 3 | ```jsx 4 | // A normal Text Component 5 | import React from 'react'; 6 | 7 | export default function Text(props) { 8 | const { text, ...restProps } = props; 9 | // You have no idea what to expect in restProps as it's passed down from parent 10 | return
{ text }
11 | } 12 | ``` 13 | 14 | ```jsx 15 | import React, { Fragment } from 'react'; 16 | import ReactDOM from 'react-dom'; 17 | 18 | export default function withHighlight(Comp) { 19 | return (props) => { 20 | return 21 | } 22 | } 23 | 24 | // what's the color ? style overwritten 25 | const Comp = withHighlight(Text); 26 | ReactDom.render( 27 | 28 | 29 | 30 | , 31 | document.getElementById('root') 32 | ) 33 | ``` 34 | 35 | > Solution: merge props if necessary 36 | 37 | ```jsx 38 | // A normal Text Component 39 | import React from 'react'; 40 | 41 | export default function Text(props) { 42 | const { text, style, ...restProps } = props; 43 | return
44 | { text } 45 |
46 | } 47 | ``` 48 | 49 | ## Notice 50 | 51 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 52 | -------------------------------------------------------------------------------- /articles/Sticky footer.md: -------------------------------------------------------------------------------- 1 | # Sticky footer 2 | 3 | > A sticky footer pattern is one where the footer of your page 'sticks' to the bottom of the browser window. 4 | 5 | - Footer sticks to the bottom of the viewport when content is short 6 | - If the content of the page extends past the viewport bottom, the footer then sits below the content as normal 7 | 8 | ```html 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
Header
17 |
Main
18 | 19 |
20 | 21 | 22 | ``` 23 | 24 | ```css 25 | .container { 26 | display: flex; 27 | flex-direction: column; 28 | height: 100vh; 29 | } 30 | 31 | .header { 32 | height: 100px; 33 | background-color: red; 34 | flex-shrink: 0; 35 | } 36 | 37 | .main { 38 | flex: 1 0 auto; 39 | background-color: green; 40 | /* height: 1100px; */ 41 | } 42 | 43 | .footer { 44 | height: 100px; 45 | background-color: teal; 46 | flex-shrink: 0; 47 | } 48 | ``` 49 | 50 | [Code Example](https://codesandbox.io/s/sticky-footer-d56em) 51 | 52 | ## Notice 53 | 54 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe -------------------------------------------------------------------------------- /articles/Switch statement.md: -------------------------------------------------------------------------------- 1 | Two small techniques/hints for `switch` statement in JS/TS. 2 | 3 | # Curly braces after `case` 4 | 5 | Curly braces after `case` establishes their own block scope, where you can define local variables. 6 | 7 | ## Error 8 | ```ts 9 | function foo(x: number) { 10 | switch (x) { 11 | case 1: 12 | let a = 1; 13 | break; 14 | case 2: 15 | let a = 2; // error, duplicate variable `a` 16 | break; 17 | } 18 | } 19 | ``` 20 | 21 | ## Ok 22 | 23 | ```ts 24 | function foo(x: number) { 25 | switch (x) { 26 | case 1: { 27 | let a = 1; 28 | break; 29 | } 30 | case 2: { 31 | let a = 2; // ok, each case has its own scope 32 | break; 33 | } 34 | } 35 | } 36 | ``` 37 | 38 | # Is any cases matched? 39 | 40 | Make use of `default` to check whether any `case` statement has been matched. E.g. 41 | 42 | ```ts 43 | function isMatched(x: number) { 44 | const hasMatch = true; 45 | switch (x) { 46 | case 1: { 47 | // xxxx 48 | break; 49 | } 50 | case 2: { 51 | // yyyy 52 | break; 53 | } 54 | default: { 55 | hasMatch = false; 56 | } 57 | } 58 | return hasMatch; 59 | } 60 | ``` 61 | 62 | ## Notice 63 | 64 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/Tedder - a scrum git branch manager.md: -------------------------------------------------------------------------------- 1 | ![Logo](../images/tedder.png) 2 | 3 | > **Tedder - a scrum git branch manager** 4 | 5 | ## Background 6 | 7 | In my team we have been adopting scrum to manage work items. We set a sprint to be one week length and we have a git sprint branch which is used to release the work done in the sprint. 8 | 9 | ## Why 10 | 11 | - We do have a naming standard for the sprint branch but it's sometimes violated for some reasons, leading to inconsistent branches. Our naming standard is __feature/[yyyy][mm][dd]__, therefore the branch should be __feature/20180809__ for the sprint ending in 9th Aug 2018. I do see these kinds of violations: 12 | - feature/0809 - missing year 13 | - feature/180809 - short year 14 | - featuer/20180809 - tries to follow the standard but makes a typo 15 | 16 | - If more than one developer participates in the sprint, they usually need to hang around asking whether the branch has been created since only one branch needs creating, leading to unnecessary communication and development interruption. 17 | 18 | ## How 19 | 20 | - For detail usage, refers to the [docs](https://github.com/n0ruSh/tedder) 21 | - Basically you would want to setup a config file in your git repo: 22 | 23 | ``` 24 | // .tedderrc 25 | { 26 | "day": "Thu", 27 | "template": "feat/[yyyy][mm][dd]" 28 | } 29 | 30 | ``` 31 | 32 | Then when you run `tedder` command in your repo, it will compute the branch name based on your specificed template and day. In our exmaple above, it will be __feat/[yyyy][mm][dd]__ with __yyyy__, 33 | __mm__ and __dd__ being substituted with the full year, month and date of next Thursday. It will then check the corresponding remote branch exists or not. If the corresponding remote branch exists, meaning others have created the branch before, it will simple fetch the remote branch and switch to the branch. Otherwiese, it will automatically create the branch for you and push it to remote repo. 34 | 35 | You can also set a script in your package.json to use it locally. 36 | 37 | ``` 38 | //package.json 39 | 40 | { 41 | script: { 42 | "scrum": "tedder" 43 | } 44 | } 45 | 46 | ``` 47 | 48 | Then you can simply do 49 | 50 | ``` 51 | npm run scrum 52 | ``` 53 | 54 | - You can also use it globally from command line with options. 55 | 56 | ``` 57 | tedder -t 'hotfix-[yy][mm][dd]' -d Mon -n 1 58 | ``` 59 | 60 | This will setup the hotfix branch for next Monday for you. -------------------------------------------------------------------------------- /articles/Transition, transform and animation.md: -------------------------------------------------------------------------------- 1 | # [Transition](https://developer.mozilla.org/en-US/docs/Web/CSS/transition) 2 | 3 | > Transitions enable you to define the transition between two states of an element when the state changes. 4 | 5 | The `transition` CSS property is a shorthand property for 6 | 7 | * [transition-property](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-property) 8 | * [transition-duration](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-duration) 9 | * [transition-timing-function](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function) 10 | * [transition-delay](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-delay) 11 | 12 | ## Syntax 13 | 14 | ```css 15 | transition: = [ none | ] ||

54 | See the Pen 55 | Button with transition by 不吃猫的鱼 (@n0rush) 56 | on CodePen. 57 |

58 | 59 | 60 | # [Transform](https://developer.mozilla.org/en-US/docs/Web/CSS/transform) 61 | 62 | 63 | > The `transform` CSS property lets you rotate, scale, skew, or translate an element. It modifies the coordinate space of the CSS visual formatting model. 64 | 65 | ## [Rotate](https://developer.mozilla.org/en-US/docs/Web/CSS/rotate) 66 | 67 | > The `rotate` CSS property allows you to specify rotation transforms individually and independently of the transform property 68 | 69 | ```html 70 |
Rotate 30deg
71 |
Rotate -30deg
72 | ``` 73 | 74 | ```css 75 | * { 76 | box-sizing: border-box; 77 | } 78 | 79 | .item { 80 | background-color: teal; 81 | width: 100px; 82 | height: 100px; 83 | display: flex; 84 | align-items: center; 85 | padding: 10px; 86 | margin: 50px; 87 | } 88 | 89 | .thirty { 90 | transform: rotate(30deg); 91 | } 92 | 93 | .minus-thirty { 94 | transform: rotate(-30deg); 95 | } 96 | ``` 97 | 98 |

99 | See the Pen 100 | rotate by 不吃猫的鱼 (@n0rush) 101 | on CodePen. 102 |

103 | 104 | 105 | ### Making a badge 106 | 107 | ```html 108 |
109 |
Prime
110 |
111 | ``` 112 | 113 | ```css 114 | * { 115 | box-sizing: border-box; 116 | } 117 | 118 | .card { 119 | background-color: teal; 120 | width: 200px; 121 | height: 100px; 122 | margin: 50px; 123 | position: relative; 124 | overflow: hidden; 125 | } 126 | 127 | .badge { 128 | position: absolute; 129 | background-color: #dfc9c9; 130 | width: 80px; 131 | height: 15px; 132 | font-size: 13px; 133 | color: blue; 134 | left: 0; 135 | top: 0; 136 | text-align: center; 137 | transform: rotate(-45deg) translateX(-23px) 138 | } 139 | ``` 140 | 141 |

142 | See the Pen 143 | badge with rotate by 不吃猫的鱼 (@n0rush) 144 | on CodePen. 145 |

146 | 147 | 148 | ## [Translate](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/translate) 149 | 150 | > The translate() CSS function repositions an element in the horizontal and/or vertical directions. Its result is a data type. 151 | 152 | ```html 153 |
154 |
translateX(30px)
155 |
156 |
157 |
translateY(30px)
158 |
159 |
160 |
translateZ(50px) -> closer to user -> getting larger
161 |
162 | ``` 163 | 164 | ```css 165 | * { 166 | box-sizing: border-box; 167 | } 168 | 169 | .wrapper { 170 | border: 1px dashed black; 171 | margin-bottom: 100px; 172 | } 173 | 174 | .item { 175 | background-color: teal; 176 | width: 100px; 177 | height: 100px; 178 | } 179 | 180 | .x-30 { 181 | transform: translateX(30px) 182 | } 183 | 184 | .y-30 { 185 | transform: translateY(30px) 186 | } 187 | 188 | .z-50 { 189 | font-size: 14px; 190 | transform: perspective(500px) translateZ(50px) 191 | } 192 | ``` 193 | 194 |

195 | See the Pen 196 | transform by 不吃猫的鱼 (@n0rush) 197 | on CodePen. 198 |

199 | 200 | 201 | ## [Scale](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/scale) 202 | 203 | >The scale() CSS function defines a transformation that resizes an element on the 2D plane. Because the amount of scaling is defined by a vector, it can resize the horizontal and vertical dimensions at different scales 204 | 205 | > A transform is made around a point of origin. This point servers as the axis of rotation, or the spot where scaling or skewing begins. 206 | 207 | ```html 208 |
209 |
scale(0.5)
210 |
211 | 212 |
213 |
scale(0.5, 0.7)
214 |
215 | 216 |
217 |
origin(0,0) scale(0.5)
218 |
219 | ``` 220 | 221 | ```css 222 | * { 223 | box-sizing: border-box; 224 | } 225 | 226 | .wrapper { 227 | border: 1px dashed black; 228 | width: 200px; 229 | height: 200px; 230 | position: relative; 231 | margin-bottom: 20px; 232 | } 233 | 234 | .item { 235 | background-color: rgba(209, 194, 194, 0.3); 236 | background-clip: padding-box; 237 | width: 200px; 238 | height: 200px; 239 | position: absolute; 240 | left: -1px; 241 | top: -1px; 242 | line-height: 200px; 243 | text-align: center; 244 | } 245 | 246 | .one { 247 | transform: scale(0.5); 248 | } 249 | 250 | .three { 251 | transform: scale(0.5); 252 | transform-origin: 0 0 253 | } 254 | ``` 255 | 256 |

257 | See the Pen 258 | scale by 不吃猫的鱼 (@n0rush) 259 | on CodePen. 260 |

261 | 262 | 263 | ## [Skew](https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/skew) 264 | 265 | > The skew() CSS function defines a transformation that skews an element on the 2D plane 266 | 267 | 268 | ```html 269 |
270 |
skew(15deg)
271 |
272 | ``` 273 | 274 | ```css 275 | * { 276 | box-sizing: border-box; 277 | } 278 | 279 | .wrapper { 280 | border: 1px dashed black; 281 | width: 200px; 282 | height: 200px; 283 | position: relative; 284 | margin-bottom: 20px; 285 | } 286 | 287 | .item { 288 | background-color: rgba(209, 194, 194, 0.3); 289 | background-clip: padding-box; 290 | width: 200px; 291 | height: 200px; 292 | position: absolute; 293 | left: -1px; 294 | top: -1px; 295 | line-height: 200px; 296 | text-align: center; 297 | } 298 | 299 | .one { 300 | transform: skew(15deg) 301 | } 302 | ``` 303 | 304 |

305 | See the Pen 306 | skew by 不吃猫的鱼 (@n0rush) 307 | on CodePen. 308 |

309 | 310 | 311 | # [Animation](https://developer.mozilla.org/en-US/docs/Web/CSS/animation) 312 | 313 | > The animation shorthand CSS property applies an animation between styles. 314 | 315 | It is a shorthand for 316 | 317 | * [animation-name](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-name) 318 | * [animation-duration](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-duration) 319 | * [animation-timing-function](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function) 320 | * [animation-delay](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-delay) 321 | * [animation-iteration-count](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-iteration-count) 322 | 323 | Animations in CSS contain two parts: the `@keyframes` at rule, which defines an animation, and the `animation` property, which applies that animation to an element. 324 | 325 | ```html 326 |
327 | ``` 328 | 329 | ```css 330 | @keyframes over-and-back { 331 | 0% { 332 | background-color: hsl(0, 50%, 50%); 333 | transform: translate(0) 334 | } 335 | 336 | 50% { 337 | transform: translate(100px) 338 | } 339 | 340 | 100% { 341 | background-color: hsl(270, 50%, 90%); 342 | transform: translate(0) 343 | } 344 | } 345 | 346 | .box { 347 | width: 100px; 348 | height: 100px; 349 | background-color: red; 350 | animation: over-and-back 3s ease-in 3; 351 | } 352 | ``` 353 | 354 |

355 | See the Pen 356 | animation by 不吃猫的鱼 (@n0rush) 357 | on CodePen. 358 |

359 | 360 | 361 | ## Flying card 362 | 363 | ```html 364 |
365 |

Cluck Norries

366 |

Every brood has its brawler. Cluck Norris is our feistiest hen, frequently picking fights with other hens about laying territory and foraging space

367 | ``` 368 | 369 | ```css 370 | .flying-card { 371 | border: 1px solid black; 372 | max-width: 300px; 373 | padding: 10px; 374 | box-sizing: border-box; 375 | box-shadow: 0.2em 0.5em 1em rgba(0,0,0,0.3); 376 | animation: fly-in 2s ease-in; 377 | perspective: 500px; 378 | } 379 | 380 | p { 381 | color: hsl(210, 15%, 20%) 382 | } 383 | 384 | @keyframes fly-in { 385 | 0% { 386 | transform: translateZ(-800px) rotateY(90deg); 387 | opacity: 0; 388 | } 389 | 56% { 390 | transform: translateZ(-160px) rotateY(80deg); 391 | opacity: 1; 392 | } 393 | 100% { 394 | transform: translateZ(0) rotateY(0); 395 | } 396 | } 397 | ``` 398 | 399 |

400 | See the Pen 401 | flying card by 不吃猫的鱼 (@n0rush) 402 | on CodePen. 403 |

404 | 405 | 406 | ## Notice 407 | 408 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/Two Css properties you may NOT know.md: -------------------------------------------------------------------------------- 1 | # [user-select](https://developer.mozilla.org/en-US/docs/Web/CSS/user-select) 2 | 3 | > The user-select CSS property controls whether the user can select text. 4 | 5 | ```html 6 |

Normal selection

7 |

None selection

8 |

All selection

9 | ``` 10 | 11 | ```css 12 | .select-none { 13 | user-select: none; 14 | } 15 | 16 | .select-all { 17 | user-select: all; 18 | } 19 | ``` 20 | 21 | [code sample](https://codesandbox.io/s/use-select-lp7u9) 22 | 23 | # [pointer-events](https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events) 24 | 25 | > The pointer-events CSS property sets under what circumstances (if any) a particular graphic element can become the target of pointer events. 26 | 27 | ```html 28 | 32 | ``` 33 | 34 | [code sample](https://codesandbox.io/s/point-events-6gp7x?file=/index.html) 35 | 36 | ## Notice 37 | 38 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/Typescript introduction(IV).md: -------------------------------------------------------------------------------- 1 | # Modules 2 | 3 | ## Export 4 | 5 | ### Export declaration 6 | 7 | ```javascript 8 | export const num = 1; 9 | ``` 10 | 11 | ### Export statement 12 | 13 | ```javascript 14 | 15 | // a.js 16 | const num = 1; 17 | const str = 'export'; 18 | export { num }; 19 | export { str as exportStr }; // export renaming 20 | ``` 21 | 22 | ### Export with default 23 | 24 | ```javascript 25 | // b.js 26 | export default function toString(obj) { 27 | return obj.toString(); 28 | } 29 | ``` 30 | 31 | ### Re-exports 32 | 33 | ```javascript 34 | export { num, exportStr } from './a.js' 35 | // equivalent to 36 | export * from './a.js' 37 | ``` 38 | 39 | ## Import 40 | 41 | ```javascript 42 | import { num, exportStr } from './a.js'; 43 | import * as util from './a.js' 44 | 45 | 46 | import toString from './b.js'; 47 | // equivalent to 48 | import { default as toString } from './b.js' 49 | ``` 50 | 51 | ## `module` in tsconfig 52 | 53 | Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. 54 | 55 | # Namespace 56 | 57 | > Ambient declarations are written using the declare keyword and can declare variables, functions, classes, enums, namespaces, or modules. 58 | 59 | ## Ambient modules 60 | 61 | ```javascript 62 | // node.d.ts 63 | declare module "url" { 64 | export interface Url { 65 | protocol?: string; 66 | hostname?: string; 67 | pathname?: string; 68 | } 69 | 70 | export function parse(urlStr: string, parseQueryString?): Url; 71 | } 72 | ``` 73 | 74 | ```javascript 75 | /// 76 | import * as URL from "url"; 77 | let myUrl = URL.parse("https://github.com/n0rush"); 78 | ``` 79 | 80 | ## Shorthand ambient modules 81 | 82 | ```javascript 83 | declare module "new-module"; // All imports from a shorthand module will have the any type. 84 | ``` 85 | 86 | ## Wildcard module declarations 87 | 88 | ```javascript 89 | declare module "json!*" { 90 | export const version: string; 91 | const value: any; 92 | export default value; 93 | } 94 | ``` 95 | 96 | cant only do 97 | 98 | ```javascript 99 | import { version } from 'example.json' 100 | import data from 'example.json' 101 | import { data } from 'example.json' // error 102 | ``` 103 | 104 | ## Namespaces produce values 105 | 106 | ```javascript 107 | 108 | namespace N { 109 | let str = "hello world"; 110 | export function fn() { 111 | return str; 112 | } 113 | } 114 | 115 | N.fn(); 116 | N.str; // Error, str is not exported 117 | ``` 118 | 119 | The above `N` declaration is equivalent to the folowing in compiled code: 120 | 121 | ```javascript 122 | var N; 123 | (function (N) { 124 | var str = "hello world"; 125 | function fn() { 126 | return str; 127 | } 128 | N.fn = fn; 129 | })(N || (N = {})); 130 | N.fn(); 131 | N.str; // Error, str is not exported 132 | ``` 133 | 134 | 135 | # Module resolution 136 | 137 | ```javascript 138 | import { a } from 'pathOfModuleA'; 139 | ``` 140 | 141 | * First, the compiler will try to locate a file that represents the imported module. To do so the compiler follows one of two different strategies: Classic or Node, which is specified with `moduleResolution` in tsconfig. 142 | * If no match is found and the module name is non-relative, then the compiler will attempt to locate an ambient module declaration. 143 | * A non-relative import can be resolved relative to baseUrl, or through path mapping. They can also resolve to ambient module declarations. Use non-relative paths when importing any of your external dependencies. 144 | 145 | ## Base Url 146 | 147 | Setting baseUrl informs the compiler where to find modules. All module imports with non-relative names are assumed to be relative to the baseUrl. Note that relative module imports are not impacted by setting the baseUrl, as they are always resolved relative to their importing files. 148 | 149 | ## Path mapping, equivalent to alias in other tools 150 | 151 | ```javascript 152 | { 153 | "compilerOptions": { 154 | "baseUrl": ".", // This must be specified if "paths" is set. 155 | "paths": { 156 | "jquery": ["node_modules/jquery/dist/jquery"] // This mapping is relative to "baseUrl" 157 | } 158 | } 159 | } 160 | ``` 161 | 162 | ## Notice 163 | 164 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 165 | -------------------------------------------------------------------------------- /articles/Typescript introduction(Ⅰ).md: -------------------------------------------------------------------------------- 1 | ## What is Typescript 2 | 3 | > Typescript - Javascript that scales 4 | 5 | ## Why Typescript 6 | 7 | ### Static type system 8 | 9 | TypeScript can do static type checking at compile time. Types are erased before emitting compiled Javascript, result in zero run-time overhead to program execution. 10 | 11 | ### Scalability 12 | 13 | It's pretty useful for large-scale system and makes code refactoring easier and under control. E.g. 14 | 15 | ```javascript 16 | // You have no idea what parameter the greet funciton accpets without peaking into the function implementation 17 | function greet(person) { 18 | if (person.sex === 'male') { 19 | return `Mr ${person.first} ${person.second}` 20 | } else if (person.sex === 'female') { 21 | return `Mis ${person.first} ${person.second}` 22 | } 23 | return `${person.first} ${person.second}` 24 | } 25 | ``` 26 | 27 | compared to the Typescript version 28 | 29 | ```typescript 30 | interface Person { 31 | sex?: string 32 | first: string 33 | second: string 34 | } 35 | 36 | function greet(person: Person): string { 37 | // ... 38 | } 39 | ``` 40 | 41 | ### IDE support - symbol based navigation + statement completion 42 | 43 | 44 | ## Starter 45 | 46 | In Typescript, types can be associated with variables through explicit type annotations, such as 47 | 48 | ```tyepscript 49 | let x: number; 50 | ``` 51 | 52 | or through implicit type inference, as in 53 | 54 | ```typescript 55 | let x = 1; 56 | ``` 57 | 58 | ### Types and Values 59 | 60 | Typescript is a superset of Javascript. What works in Javascript should also work in Typescript. The addon from Typescript, as its name indicates, is `type`. In Typescript, there are `values` and `types`. TypeScript erases all `types` information before emiting JavaScript while `values` are preserved as in Javascript. 61 | 62 | #### What results in `types`: 63 | 64 | - A type alias declaration (type sn = number | string;) 65 | - An interface declaration (interface I { x: number[]; }) 66 | - A class declaration (class C { }) 67 | - An enum declaration (enum E { A, B, C }) 68 | - An import declaration which refers to a type 69 | 70 | #### What results in `values`: 71 | 72 | - let, const, and var declarations 73 | - A namespace or module declaration which contains a value 74 | - An enum declaration 75 | - A class declaration 76 | - An import declaration which refers to a value 77 | - A function declaration 78 | 79 | ## Basic Types 80 | 81 | * Boolean 82 | 83 | ```typescript 84 | let isChecked: boolean = false; 85 | ``` 86 | 87 | * Number 88 | 89 | ```typescript 90 | let num: number = 6; 91 | ``` 92 | 93 | * String 94 | 95 | ```typescript 96 | let color: string = "blue"; 97 | ``` 98 | 99 | * Array 100 | 101 | ```typescript 102 | let list: number[] = [1, 2, 3]; 103 | 104 | let list: Array = [1, 2, 3]; // This doesnt work in JSX as `<` and `>` are used in element tag 105 | ``` 106 | 107 | * Tuple 108 | 109 | ```typescript 110 | // Declare a tuple type 111 | let x: [string, number]; 112 | // Initialize it 113 | x = ["hello", 10]; // OK 114 | // Initialize it incorrectly 115 | x = [10, "hello"]; // Error 116 | ``` 117 | 118 | * Enum 119 | 120 | ```typescript 121 | enum Color {Red = 1, Green, Blue} 122 | let c: Color = Color.Green; 123 | ``` 124 | 125 | > Note that `Enum` produce both a `value` and a `type`. 126 | 127 | ``` 128 | // compiled code 129 | var Color; 130 | (function (Color) { 131 | Color[Color["Red"] = 1] = "Red"; 132 | Color[Color["Green"] = 2] = "Green"; 133 | Color[Color["Blue"] = 3] = "Blue"; 134 | })(Color || (Color = {})); 135 | var c = Color.Green; 136 | ``` 137 | 138 | * Any - try to avoid 139 | 140 | It's useful when migrate from Javascript to Typescript. 141 | 142 | * Void 143 | 144 | `void` is a little like the opposite of `any`: the absence of having any type at all. You may commonly see this as the return type of functions that do not return a value: 145 | 146 | ```typescript 147 | function warnUser(): void { 148 | console.log("This is my warning message"); 149 | } 150 | ``` 151 | 152 | * Object: 153 | 154 | > Note: `object` is not the same as `Object`. 155 | 156 | * `object` is a basic type that represents the non-primitive type, i.e. any thing that is not number, string, boolean, symbol, null, or undefined. 157 | * `Object` is a built-in interface as shown below. It is almost any type. 158 | 159 | ``` 160 | interface Object { 161 | /** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */ 162 | constructor: Function; 163 | 164 | /** Returns a string representation of an object. */ 165 | toString(): string; 166 | 167 | /** Returns a date converted to a string using the current locale. */ 168 | toLocaleString(): string; 169 | 170 | /** Returns the primitive value of the specified object. */ 171 | valueOf(): Object; 172 | 173 | /** 174 | * Determines whether an object has a property with the specified name. 175 | * @param v A property name. 176 | */ 177 | hasOwnProperty(v: PropertyKey): boolean; 178 | 179 | /** 180 | * Determines whether an object exists in another object's prototype chain. 181 | * @param v Another object whose prototype chain is to be checked. 182 | */ 183 | isPrototypeOf(v: Object): boolean; 184 | 185 | /** 186 | * Determines whether a specified property is enumerable. 187 | * @param v A property name. 188 | */ 189 | propertyIsEnumerable(v: PropertyKey): boolean; 190 | } 191 | ``` 192 | 193 | Check the [demo](https://www.typescriptlang.org/play/index.html#src=function%20logWithBasicObject(obj%3A%20object)%20%7B%0D%0A%20%20%20%20console.log(obj.hasOwnProperty)%3B%0D%0A%7D%0D%0A%0D%0AlogWithBasicObject(%7Ba%3A%20'b'%7D)%0D%0AlogWithBasicObject('ab')%0D%0A%0D%0A%0D%0Afunction%20logWithInterfaceObject(obj%3A%20Object)%20%7B%0D%0A%20%20%20%20console.log(obj.hasOwnProperty)%3B%0D%0A%7D%0D%0A%0D%0AlogWithInterfaceObject(%7Ba%3A%20'b'%7D)%0D%0AlogWithInterfaceObject('ab')) for difference: 194 | 195 | ``` 196 | function logWithBasicObject(obj: object) { 197 | console.log(obj.hasOwnProperty); 198 | } 199 | 200 | logWithBasicObject({a: 'b'}); // OK 201 | logWithBasicObject('ab'); // Error: Argument of type '"ab"' is not assignable to parameter of type 'object' 202 | 203 | function logWithInterfaceObject(obj: Object) { 204 | console.log(obj.hasOwnProperty); 205 | } 206 | 207 | logWithInterfaceObject({a: 'b'}); // OK 208 | logWithInterfaceObject('ab'); // OK 209 | ``` 210 | 211 | ## Notice 212 | 213 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 214 | -------------------------------------------------------------------------------- /articles/Typescript introduction(ⅠII).md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | ## Function Types 4 | 5 | Function types can be defined in three ways. 6 | 7 | ### Method Signature 8 | 9 | ```typescript 10 | 11 | // function declaration 12 | function addWithMethodSignature(x: number, y: number): number { 13 | // method signature 14 | return x + y; 15 | } 16 | 17 | // function expression 18 | let anotherAddWithMethodSignature = function(x: number, y: number): number { 19 | // method signature 20 | return x + y; 21 | }; 22 | ``` 23 | 24 | ### Function type literal 25 | 26 | > A function type literal specifies the type parameters, regular parameters, and return type of a call signature. 27 | 28 | ```typescript 29 | let addWithFuncLiteral: (x: number, y: number) => number = function(x, y) { 30 | return x + y; 31 | }; 32 | ``` 33 | 34 | ### Object literal 35 | 36 | > An object type containing one or more call signatures is said to be a function type. 37 | 38 | ```typescript 39 | let addWithObjLiteral: { (x: number, y: number) : number } = function(x, y) { 40 | return x + y; 41 | }; 42 | ``` 43 | 44 | ## Function type literal vs Object literal 45 | 46 | * Function types may be written using function type literals or by including call signatures in object type literals. i.e. 47 | 48 | ```typescript 49 | < T1, T2, ... > ( p1, p2, ... ) => R 50 | ``` 51 | 52 | is exactly equivalent to the object type literal 53 | 54 | ```typescript 55 | { < T1, T2, ... > ( p1, p2, ... ) : R } 56 | ``` 57 | 58 | * To differentiate function types defined with object literal, check to see whether the object literal/interface contains a call signature 59 | 60 | ```typescript 61 | interface A { // an object with a property called `log` which is function type 62 | log(num: number): number; // a property 63 | } 64 | 65 | let obj: A = { 66 | log(n) { 67 | return n; 68 | } 69 | } 70 | 71 | interface A1 { // a function type. 72 | (num: number): number // call signature 73 | } 74 | 75 | let func: A1 = (n: number) => n 76 | ``` 77 | 78 | ## Optional parameters 79 | 80 | Optional property can be marked with `?` following the parameter name. E.g. 81 | 82 | ```typescript 83 | function parseInt(s: string, radix?: number): number { 84 | // ... 85 | } 86 | 87 | let result1 = parseInt('3'); // ok 88 | let result2 = parseInt('3', 2); // ok 89 | let result3 = parseInt('3', 2 , 'too many'); // ok 90 | ``` 91 | 92 | ## Overloads 93 | 94 | ```typescript 95 | let suits = ["hearts", "spades", "clubs", "diamonds"]; 96 | 97 | function pickCard(x: {suit: string; card: number; }[]): number; 98 | function pickCard(x: number): {suit: string; card: number; }; 99 | function pickCard(x): any { 100 | if (typeof x == "object") { 101 | let pickedCard = Math.floor(Math.random() * x.length); 102 | return pickedCard; 103 | } 104 | else if (typeof x == "number") { 105 | let pickedSuit = Math.floor(x / 13); 106 | return { suit: suits[pickedSuit], card: x % 13 }; 107 | } 108 | } 109 | 110 | let myDeck = [{ 111 | suit: "diamonds", card: 2 112 | }, { 113 | suit: "spades", card: 10 114 | }, { 115 | suit: "hearts", card: 4 116 | }]; 117 | 118 | let pickedCard1 = myDeck[pickCard(myDeck)]; 119 | alert("card: " + pickedCard1.card + " of " + pickedCard1.suit); 120 | 121 | let pickedCard2 = pickCard(15); 122 | alert("card: " + pickedCard2.card + " of " + pickedCard2.suit); 123 | ``` 124 | 125 | ## Notice 126 | 127 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 128 | -------------------------------------------------------------------------------- /articles/Typescript introduction(Ⅱ).md: -------------------------------------------------------------------------------- 1 | ## Interfaces 2 | 3 | * Typescript interfaces declare the structure of variables. 4 | * Interface declarations produce `types`, not `values`. 5 | * Interfaces provide the ability to name and parameterize object types. To represent primitive type, use `type` declaration 6 | 7 | ```typescript 8 | type numOrString = number | string; // union of primitive type 9 | type size = 'large' | 'small' | 'medium'; // union of string literals 10 | ``` 11 | 12 | ### Basic Interface 13 | 14 | ```typescript 15 | interface WithName { 16 | name: string; 17 | } 18 | 19 | function printName(withNameObj: WithName) { 20 | console.log(withNameObj.name); 21 | } 22 | ``` 23 | 24 | ### Type checking rule 25 | 26 | To check whether variable `y` can be assigned to variable/parameter `x`, the compiler checks each property of `x` to find a corresponding compatible property in `y`. 27 | 28 | * The same rule for assignment is used when checking function call arguments. 29 | 30 | * Object literals get `special` treatment and undergo excess property checking when assigning them to other variables, or passing them as arguments 31 | 32 | [Example](https://www.typescriptlang.org/play/index.html#src=interface%20WithName%20%7B%0D%0A%20%20name%3A%20string%3B%0D%0A%7D%0D%0A%0D%0Afunction%20printName(withNameObj%3A%20WithName)%20%7B%0D%0A%20%20console.log(withNameObj.name)%3B%0D%0A%7D%0D%0A%0D%0Alet%20myObj%20%3D%20%7Bname%3A%20'mike'%2C%20age%3A%2018%7D%3B%0D%0A%2F%2F%20myObj's%20inferred%20type%20is%20%7Bname%3A%20string%2C%20age%3A%20number%7D%3B%0D%0AprintName(myObj)%3B%20%2F%2F%20OK%0D%0A%0D%0AprintName(%7Bname%3A%20'mike'%2C%20age%3A%2018%7D)%3B%20%2F%2F%20Error%2C%20excess%20property%20checking%20for%20object%20literal%0D%0AprintName(%7Bname%3A%20'mike'%2C%20age%3A%2018%7D%20as%20WithName)%3B%20%2F%2F%20type%20assertion%20as%20fix%0D%0A%0D%0Alet%20myObj1%3A%20WithName%20%3D%20%7Bname%3A%20'mike'%2C%20age%3A%2018%7D%3B%20%2F%2F%20error%2C%20excess%20property%20checking%20for%20object%20literal%0D%0Alet%20myObj2%3A%20WithName%20%3D%20myObj%3B%20%2F%2F%20OK) 33 | 34 | ```typescript 35 | 36 | let myObj = {name: 'mike', age: 18}; 37 | // myObj's inferred type is {name: string, age: number}; 38 | printName(myObj); // OK 39 | 40 | // Error, excess property checking for object literal 41 | printName({name: 'mike', age: 18}); 42 | // type assertion as fix 43 | printName({name: 'mike', age: 18} as WithName); 44 | 45 | // error, excess property checking for object literal 46 | let myObj1: WithName = {name: 'mike', age: 18}; 47 | 48 | let myObj2: WithName = myObj; // OK 49 | ``` 50 | 51 | ### Optional property 52 | 53 | Optional property can be marked with `?` following the property name. E.g. 54 | 55 | ```typescript 56 | interface SquareConfig { 57 | color?: string; 58 | width?: number; 59 | } 60 | 61 | function createSquare(config: SquareConfig): {color: string; area: number} { 62 | let newSquare = {color: "white", area: 100}; 63 | if (config.color) { 64 | newSquare.color = config.color; 65 | } 66 | if (config.width) { 67 | newSquare.area = config.width * config.width; 68 | } 69 | return newSquare; 70 | } 71 | 72 | let mySquare = createSquare({color: "black"}); 73 | ``` 74 | 75 | ### Readonly properties 76 | 77 | Readonly properties can be mark with `readonly` modifier. [E.g.](https://www.typescriptlang.org/play/index.html#src=interface%20Person%20%7B%0D%0A%20%20%20%20readonly%20identityId%3A%20number%3B%0D%0A%20%20%20%20readonly%20bloodType%3A%20'A'%20%7C%20'B'%3B%0D%0A%7D%0D%0A%0D%0Alet%20p1%3A%20Person%20%3D%20%7B%20identityId%3A%2010000%2C%20bloodType%3A%20'A'%20%7D%3B%0D%0Ap1.identityId%20%3D%20100005%3B%20%2F%2F%20error) 78 | 79 | ```typescript 80 | interface Person { 81 | readonly identityId: number; 82 | readonly bloodType: 'A' | 'B'; 83 | } 84 | 85 | let p1: Person = { identityId: 10000, bloodType: 'A' }; 86 | p1.identityId = 100005; // error! 87 | ``` 88 | 89 | ## Classes 90 | 91 | ```typescript 92 | class Point { 93 | constructor(public x: number, public y: number) { 94 | this.x = x; 95 | this.y = y; 96 | } 97 | public length() { 98 | return Math.sqrt(this.x * this.x + this.y * this.y); 99 | } 100 | static origin = new Point(0, 0); 101 | } 102 | ``` 103 | 104 | ## `value` and `type` 105 | 106 | When you declare a class in TypeScript, you are actually creating multiple declarations at the same time. 107 | 108 | * a value - constructor 109 | * a type - the `type` of the instance of the class. 110 | 111 | > The following example introduces both a named type called `Point` (the class type) and a named value called `Point` (the constructor function) in the containing declaration space. 112 | 113 | * The named type `Point` is exactly equivalent to 114 | 115 | ```typescript 116 | interface Point { 117 | x: number; 118 | y: number; 119 | length(): number; 120 | } 121 | ``` 122 | 123 | * The named value `Point` is a constructor function whose type corresponds to the declaration 124 | 125 | ``` 126 | let Point: { 127 | new(x: number, y: number): Point; 128 | origin: Point; 129 | }; 130 | ``` 131 | 132 | > The context in which a class is referenced distinguishes between the class type and the constructor function. 133 | 134 | For example, in the assignment statement 135 | 136 | ```typescript 137 | let p: Point = new Point(10, 20); 138 | ``` 139 | 140 | the identifier `Point` in the type annotation refers to the class type, whereas the identifier `Point` in the new expression refers to the constructor function object. 141 | 142 | ## `typeof` operator 143 | 144 | The `typeof` operator takes an operand of any type and produces a value of the String primitive type. In positions where a type is expected, `typeof` can also be used in a type query to produce the type of an expression, in which case it should be followed by a `value`. 145 | 146 | ``` 147 | let x = 5; 148 | let y = typeof x; // Use in an expression, equivalent to ` let y = 'number' ` 149 | let z: typeof x; // Use in a type query, equivalent to ` let z: number ` 150 | 151 | let obj = { a: 3, b: 's' }; 152 | let another1: typeof obj = { a: 4 }; // error, property 'b' is missing 153 | ``` 154 | 155 | ## Notice 156 | 157 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 158 | -------------------------------------------------------------------------------- /articles/Understanding react key.md: -------------------------------------------------------------------------------- 1 | # Understanding React key 2 | 3 | > Keys give the react elements a stable `identity` 4 | 5 | ## warning in array without key 6 | 7 | ```jsx 8 | import React from 'react'; 9 | 10 | function App() { 11 | const persons = [‘mike’, ‘jason’, ‘sharky’]; 12 | return
    13 | {persons.map(p => { 14 | return
  • {p}
  • 15 | })} 16 |
17 | } 18 | ``` 19 | 20 | React will throw a warning with the code above: 21 | `Warning: Each child in a list should have a unique "key" prop.` 22 | 23 | ## What keys to use? 24 | 25 | Keys should be `unique` and `stable` 26 | 27 | ### Index ? Maybe, but only `under some circumstances` 28 | 29 | Array index is unique across an array. 30 | 31 | ```jsx 32 | import React, { useState } from 'react'; 33 | 34 | function App() { 35 | const persons = [‘mike’, ‘jason’, ‘sharky’]; 36 | return
    37 | {persons.map((p, index) => { 38 | return
  • {p}
  • 39 | })} 40 |
41 | } 42 | ``` 43 | 44 | However, if the order/number of items may change, array index is NOT `stable`. 45 | 46 | ```jsx 47 | import React, { useState } from 'react'; 48 | 49 | function App() { 50 | const [persons, setPersons] = useState( 51 | ['mike', 'jason', 'sharky'] 52 | ); 53 | 54 | const onAdd = () => { 55 | setPersons(['fishman', ...persons]) 56 | } 57 | return <> 58 | 59 |
    60 | {persons.map((p, index) => { 61 | return
  • {p}
  • 62 | })} 63 |
64 | 65 | } 66 | ``` 67 | 68 | [array-index-as-key demo](https://codesandbox.io/s/staging-resonance-vy7eq) 69 | 70 | Ticking `mike` and then clicking the add button will result in the loss of check status of `mike` 71 | 72 | ### `Stable` key 73 | 74 | Usually we will have a unique and stable `id` like property for each record from backend/database. 75 | 76 | ```jsx 77 | import React, { useState } from ‘react’; 78 | 79 | export default function App() { 80 | const [persons, setPersons] = useState( 81 | [ 82 | {id: 'uywoejk', name: 'mike'}, 83 | {id: 'woeioqj', name: 'jason'}, 84 | {id: 'eljlkqd', name: 'sharky'} 85 | ] 86 | ); 87 | 88 | const onAdd = () => { 89 | setPersons([{ 90 | id: 'wuioeioe', name: 'fishman' 91 | }, ...persons]) 92 | } 93 | return <> 94 | 95 |
    96 | { 97 | persons.map((p, index) => { 98 | return
  • {p.name}
  • 99 | }) 100 | } 101 |
102 | 103 | } 104 | ``` 105 | 106 | [stable-key demo](https://codesandbox.io/s/gallant-visvesvaraya-0b3lo) 107 | 108 | ## Use key to unmount component 109 | 110 | Use case: I have a select and input component. Each the select option changes, reset the input to default state, 111 | 112 | ```jsx 113 | import React, { useState } from “react”; 114 | 115 | export default function App() { 116 | const [option, setOption] = useState(""); 117 | return ( 118 | <> 119 | 123 | 124 | 125 | 126 | ); 127 | } 128 | ``` 129 | 130 | [react-key demo](https://codesandbox.io/s/react-key-yth0y?file=/src/App.js:0-97) 131 | 132 | ## Conclusion 133 | 134 | * `key` is an unique identifier for react element 135 | * `key` should be `unique` and `stable`. A react element with different `key` in different render phases will be considered as different elements. The old one will be unmounted and a new one is created. 136 | * Only use array indices as keys when the number/order of array items are unchanged across the whole app lifecycle. 137 | * Always prefer to use backend/database unique identifier id like property as `key` 138 | 139 | ## Notice 140 | 141 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 142 | -------------------------------------------------------------------------------- /articles/Ways to iterate through objects.md: -------------------------------------------------------------------------------- 1 | ## Relevant basic concepts 2 | 3 | ### Property accessors 4 | 5 | ```javascript 6 | let obj = { 7 | a: 'b' 8 | }; 9 | 10 | // dot notation 11 | console.log(obj.a); // 'b' 12 | // bracket notation 13 | console.log(obj['a']); // 'b' 14 | // through prototype chain 15 | console.log(obj.toString, obj.toString === Object.prototype.toString); // ƒ toString() { [native code] } true 16 | ``` 17 | 18 | ### Check whether an property exists in object 19 | 20 | * [in operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in): returns true if the specified property is in the specified object *or its prototype chain*. 21 | * [hasOwnProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty): returns a boolean indicating whether the object has the specified property as its own property (as opposed to inheriting it). 22 | 23 | ```javascript 24 | let obj = { 25 | a: 'b' 26 | }; 27 | 28 | console.log('a' in obj); //true 29 | console.log('toString' in obj); //true 30 | console.log(obj.hasOwnProperty('a')); //true 31 | console.log(obj.hasOwnProperty('toString')); //false 32 | ``` 33 | 34 | ### [Object.defineProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) && [Object.defineProperties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties) - defines new or modifies existing properties directly on an object, returning the object. 35 | 36 | ```javascript 37 | let obj = { 38 | a: "b" 39 | }; 40 | 41 | Object.defineProperties(obj, { 42 | c: { 43 | value: 'd' 44 | }, 45 | e: { 46 | value: 'f' 47 | } 48 | }); 49 | console.log(obj); // {a: "b", c: "d", e: "f"} 50 | ``` 51 | 52 | #### [getter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get) and [setter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set) 53 | 54 | We can use getters and setters to generate computed property. E.g. 55 | 56 | ``` 57 | let people = { 58 | firstName: 'michael', 59 | lastName: 'zheng', 60 | get fullName() { 61 | return `${this.firstName} ${this.lastName}` 62 | }, 63 | set fullName(val) { 64 | [this.firstName, this.lastName] = val.split(' '); 65 | } 66 | } 67 | 68 | console.log(people.firstName, people.lastName, people.fullName); 69 | //"michael", "zheng", "michael zheng" 70 | people.fullName = 'hello world'; 71 | console.log(people.firstName, people.lastName, people.fullName); 72 | //"hello", "world", "hello world" 73 | ``` 74 | 75 | ## There are three ways to iterate through objects 76 | 77 | ```javascript 78 | let student = { 79 | name: "michael" 80 | }; 81 | 82 | Object.defineProperties(student, { 83 | age: { 84 | enumerable: false, 85 | value: 18 86 | }, 87 | grade: { 88 | value: 6, 89 | enumerable: true 90 | }, 91 | sex: { 92 | value: "male", 93 | enumerable: false 94 | } 95 | }); 96 | 97 | Object.prototype.x = "inherited"; 98 | ``` 99 | 100 | In the sample above, we create an object *student*. *student* has following properties: 101 | 102 | * _name_: self enumerable property 103 | * _age_: self non-enumerable property 104 | * _grade_: self enumerable property 105 | * _sex_: self non-enumerable property 106 | 107 | as well as a custom property from prototype chain: 108 | 109 | * _x_: enumerable 110 | 111 | ### [for...in](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in) iterates over *enumerable* properties of an object, **including prototype chain**. 112 | 113 | ```javascript 114 | for (let prop in student) { 115 | console.log(prop); //'name', 'grade', 'x' 116 | } 117 | 118 | for (let prop in student) { // self properties only 119 | if (student.hasOwnProperty(prop)) { 120 | console.log(prop); // 'name', 'grade' 121 | } 122 | } 123 | ``` 124 | 125 | ### [Object.keys](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) returns an array of a given object's property names, only iterate through self enumerable properties.(i.e. **not including prototype chain**) 126 | 127 | ```javascript 128 | console.log(Object.keys(student)); // [‘name’, 'grade'] 129 | 130 | //check whether is plain object: 131 | Object.keys(student).length === 0; //false 132 | 133 | Object.keys({}).length === 0; //true 134 | ``` 135 | 136 | ### [Object.getOwnPropertyNames](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames) returns an array of all self properties (including non-enumerable properties) found directly upon a given object. 137 | 138 | ```javascript 139 | // will not iterate through prototype chain 140 | console.log(Object.getOwnPropertyNames(student)); // [‘name’, ‘age’, 'grade', 'sex'] 141 | ``` 142 | 143 | ### Summarize 144 | 145 | | methods | through prototype chain | enumerable only | 146 | | :---: | :---: | :---: | 147 | | for...in | Y | Y | 148 | | Object.keys | N | Y | 149 | | Object.getOwnPropertyNames | N | N | 150 | 151 | 152 | ## Notice 153 | 154 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 155 | -------------------------------------------------------------------------------- /articles/When and why would I want to use SFC declaration.md: -------------------------------------------------------------------------------- 1 | # When and why would I want to use SFC declaration 2 | 3 | ## Ok way 4 | 5 | Normally, when defining a react component in typescript, we only specify the `props` and leave the typescript to figure out the component/function type. E.g. 6 | 7 | ```typescript 8 | // interface.ts 9 | import { ReactNode, CSSProperties } from 'react'; 10 | 11 | export interface IContainerProps { 12 | children: ReactNode; 13 | style?: CSSProperties; 14 | } 15 | ``` 16 | 17 | ```typescript 18 | // container.tsx - only define props 19 | import React from 'react'; 20 | import { IContainerProps } from './interface.ts' 21 | export default function Container(props: IContainerProps) { 22 | const { children, style = {}} = props; 23 | return ( 24 |
25 | {children} 26 |
27 | ); 28 | } 29 | ``` 30 | 31 | `Container` would then be inferred with the type `function Container(props: IContainerProps): JSX.Element` 32 | 33 | ### ReactElement 34 | 35 | ```typescript 36 | interface ReactElement

= string | JSXElementConstructor> { 37 | type: T; 38 | props: P; 39 | key: Key | null; 40 | } 41 | ``` 42 | 43 | `

abc
` is a ReactElement as it is indeed equivalent to: 44 | 45 | ```javascript 46 | { 47 | $$typeof: Symbol(react.element), 48 | props: { children: 'abc' }, 49 | type: 'div', 50 | ... 51 | } 52 | ``` 53 | 54 | ### ReactNode 55 | 56 | ```typescript 57 | type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined; 58 | 59 | type ReactChild = ReactElement | ReactText; 60 | type ReactText = string | number 61 | type ReactFragment = {} | ReactNodeArray 62 | interface ReactNodeArray extends Array {} 63 | ``` 64 | 65 | ## Better way 66 | 67 | Notice that we have to define `children` prop in `IContainerProps`, we can optimize this with `SFC` 68 | 69 | ```typescript 70 | import React, { SFC } from 'react'; 71 | import { IContainerProps } from './interface'; 72 | 73 | const Container: SFC = (props) => { 74 | const { children, style = {} } = props; 75 | return ( 76 |
77 | {children} 78 |
79 | ); 80 | } 81 | 82 | export default Container; 83 | ``` 84 | 85 | ```typescript 86 | import { CSSProperties } from 'react'; 87 | 88 | export interface IContainerProps { 89 | style?: CSSProperties; 90 | } 91 | ``` 92 | 93 | It works as `SFC` (which is basically `FunctionComponent`) has defined the `children` with generic type. 94 | 95 | ```typescript 96 | type SFC

= FunctionComponent

; 97 | 98 | interface FunctionComponent

{ 99 | (props: PropsWithChildren

, context?: any): ReactElement | null; 100 | propTypes?: WeakValidationMap

; 101 | contextTypes?: ValidationMap; 102 | defaultProps?: Partial

; 103 | displayName?: string; 104 | } 105 | ``` 106 | 107 | ```typescript 108 | type PropsWithChildren

= P & { children?: ReactNode }; 109 | ``` 110 | 111 | Note here `type` can be used to generate a new `type definition` 112 | 113 | ## Notice 114 | 115 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. 116 | -------------------------------------------------------------------------------- /articles/Why third-party cookies are NOT sent where you think they should.md: -------------------------------------------------------------------------------- 1 | ## What are third-party cookies 2 | 3 | A cookie is associated with a domain. If this domain is the same as the domain of the page you are on, the cookie is called a first-party cookie. If the domain is different, it is a third-party cookie. 4 | 5 | You can view the first-party cookies on chrome by following the procedure: Open dev console -> `Application Tab` -> `Storage` -> `Cookies`. 6 | 7 | ## Context where we need third-party cookies 8 | 9 | A cross-origin request where CORS is used. 10 | 11 | ### `OPTIONS` request 12 | 13 | A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood and a server is aware using specific methods and headers. 14 | 15 | It is an OPTIONS request, using three HTTP request headers: Access-Control-Request-Method, Access-Control-Request-Headers, and the Origin header. 16 | 17 | A preflight request is automatically issued by a browser and in normal cases. It appears when request is qualified as "to be preflighted" and omitted for simple requests. 18 | 19 | ## Cookies attributes 20 | 21 | ### httpOnly 22 | 23 | Use the `HttpOnly` attribute to prevent access to cookie values via JavaScript. It's a way of preventing XSS attack. 24 | 25 | ### Domain 26 | 27 | The `Domain` attribute specifies which hosts are allowed to receive the cookie. If unspecified, it defaults to the same origin that set the cookie, excluding subdomains. If Domain is specified, then subdomains are always included. 28 | 29 | ### Secure 30 | 31 | Cookies are only set on https. 32 | 33 | ### Same-Site 34 | 35 | The `SameSite` attribute lets servers specify whether/when cookies are sent with cross-origin requests (where Site is defined by the registrable domain), which provides some protection against cross-site request forgery attacks (CSRF). 36 | 37 | It takes three possible values: Strict, Lax, and None. With `Strict`, the cookie is sent only to the same site as the one that originated it; `Lax` is similar, except that cookies are sent when the user navigates to the cookie's origin site, for example, by following a link from an external site; `None` specifies that cookies are sent on both originating and cross-site requests, but only in secure contexts (i.e. if SameSite=None then the Secure attribute must also be set). 38 | 39 | ## Requirements for third party cookies to be sent 40 | 41 | 1. Browser settings should support third party cookies to be set. For Chrome, check the setting through the path: settings -> privacy and security -> Cookies and other site data -> Allow all cookies 42 | 2. Server correctly sets the `Same-Site` attirbute in the `Set-Cookie` response header, 43 | 3. Set `Access-Control-Allow-Credentails: true` in the `OPTIONS` prefilight request. 44 | 4. Client should explicitly includes the credentials in the request. 45 | 46 | ### Fetch 47 | 48 | ```js 49 | fetch('https://example.com', { 50 | credentials: 'include' | 'same-origin' | 'omit' 51 | }); 52 | ``` 53 | 54 | ### Axios 55 | 56 | ```js 57 | axios.defaults.withCredentials = true; 58 | ``` 59 | 60 | ## Notice 61 | 62 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe 63 | -------------------------------------------------------------------------------- /articles/Write once run anywhere with sharing components.md: -------------------------------------------------------------------------------- 1 | ## Write once, run anywhere 2 | 3 | Ever wonder writing only one piece of code but run it across all platforms (web + native)? Yes, you definitely can! 4 | 5 | ## Start from Native with [react-ative](https://facebook.github.io/react-native/) 6 | 7 | * Initiate a react native project with [react-native-cli](https://www.npmjs.com/package/react-native-cli) 8 | 9 | ```sh 10 | react-native rnweb 11 | ``` 12 | 13 | A project with the following directory structure will be created: 14 | 15 | ![Directory](https://user-images.githubusercontent.com/7504237/59961989-f4780600-9511-11e9-8462-a74979e3476b.jpg) 16 | 17 | ```js 18 | // App.js code 19 | import React, {Component} from 'react'; 20 | import {Platform, StyleSheet, Text, View} from 'react-native'; 21 | 22 | const instructions = Platform.select({ 23 | ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu', 24 | android: 25 | 'Double tap R on your keyboard to reload,\n' + 26 | 'Shake or press menu button for dev menu', 27 | }); 28 | 29 | type Props = {}; 30 | export default class App extends Component { 31 | render() { 32 | return ( 33 | 34 | Welcome to React Native! 35 | To get started, edit App.js 36 | {instructions} 37 | 38 | ); 39 | } 40 | } 41 | 42 | const styles = StyleSheet.create({ 43 | container: { 44 | flex: 1, 45 | justifyContent: 'center', 46 | alignItems: 'center', 47 | backgroundColor: '#F5FCFF', 48 | }, 49 | welcome: { 50 | fontSize: 20, 51 | textAlign: 'center', 52 | margin: 10, 53 | }, 54 | instructions: { 55 | textAlign: 'center', 56 | color: '#333333', 57 | marginBottom: 5, 58 | }, 59 | }); 60 | ``` 61 | 62 | ```js 63 | // index.js code 64 | import {AppRegistry} from 'react-native'; 65 | import App from './App'; 66 | import {name as appName} from './app.json'; 67 | 68 | AppRegistry.registerComponent(appName, () => App); 69 | ``` 70 | 71 | > You can checkout the source code at **react-native** branch of the [repo](https://github.com/n0ruSh/rnweb) 72 | 73 | * Run IOS 74 | 75 | ```sh 76 | react-native run-ios 77 | ``` 78 | 79 | ![Ios](https://user-images.githubusercontent.com/7504237/59961992-09ed3000-9512-11e9-8218-8c7baf4a0b7f.png) 80 | 81 | * Run Android 82 | 83 | ``` 84 | react-native run-android 85 | ``` 86 | ![Android](https://user-images.githubusercontent.com/7504237/59962001-1d989680-9512-11e9-9756-9989b673027f.png) 87 | 88 | ## Make it work for Web 89 | 90 | Ideally we would like our react native project to work on web as well, with minimum code modification. The goal can be achieved with the help of [react-native-web](https://github.com/necolas/react-native-web). 91 | 92 | ### Principle 93 | 94 | In the react native code, we import modules like **AppRegistry** and **Text** from [react-native](https://facebook.github.io/react-native/) package. These modules aren't recognized by web browsers and thus won't work if used directly on web platform. [react-native-web](https://github.com/necolas/react-native-web) implements the same protocols/APIs in web terminology used in [react-native](https://facebook.github.io/react-native/). By aliasing **react-native** to **react-native-web** in webpack config, we are then able to use the equivalent web components without code change. 95 | 96 | ```js 97 | //webpack.config.js 98 | resolve: { 99 | extensions: [".js", ".jsx"], 100 | alias: { 101 | "react-native": "react-native-web" 102 | } 103 | }, 104 | ``` 105 | 106 | Packaging with the above webpack config with the following code 107 | 108 | ``` 109 | import {Text} from 'react-native' 110 | ``` 111 | 112 | => actually imports **Text** component from 'react-native-web'. 113 | 114 | Let's take a look into the [source code of **Text** component implementation](https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/Text/index.js) in [react-native-web](https://github.com/necolas/react-native-web). In the **render** function the return is either a **div** or **span** element which works on web platform. 115 | 116 | ```js 117 | // Text/index.js 118 | import applyLayout from '../../modules/applyLayout'; 119 | import applyNativeMethods from '../../modules/applyNativeMethods'; 120 | import { bool } from 'prop-types'; 121 | import { Component } from 'react'; 122 | import createElement from '../createElement'; 123 | import StyleSheet from '../StyleSheet'; 124 | import TextPropTypes from './TextPropTypes'; 125 | 126 | class Text extends Component<*> { 127 | //... 128 | 129 | render() { 130 | const { 131 | dir, 132 | numberOfLines, 133 | onPress, 134 | selectable, 135 | style, 136 | /* eslint-disable */ 137 | adjustsFontSizeToFit, 138 | allowFontScaling, 139 | ellipsizeMode, 140 | lineBreakMode, 141 | minimumFontScale, 142 | onLayout, 143 | onLongPress, 144 | pressRetentionOffset, 145 | selectionColor, 146 | suppressHighlighting, 147 | textBreakStrategy, 148 | tvParallaxProperties, 149 | /* eslint-enable */ 150 | ...otherProps 151 | } = this.props; 152 | 153 | //... 154 | 155 | const component = isInAParentText ? 'span' : 'div'; 156 | 157 | return createElement(component, otherProps); 158 | } 159 | 160 | // ... 161 | } 162 | // ... 163 | 164 | export default applyLayout(applyNativeMethods(Text)); 165 | ``` 166 | 167 | > react native bundling doesn't go through webpack while web packaging does. Thus the aliasing in webpack config only works for web packaging. 168 | 169 | ### Steps Details 170 | 171 | * Add **web** folder in parellel with **android** and **ios** 172 | * Add **index.html** under **web** folder 173 | 174 | ```html 175 | 176 | 177 | 178 | React Native Web 179 | 180 | 181 | 182 |

183 | 184 | 185 | ``` 186 | * Install dependency 187 | 188 | ```sh 189 | npm install --save react-art react-dom react-native-web 190 | html-webpack-plugin webpack-dev-server webpack webpack-cli 191 | babel-loader @babel/preset-env @babel/preset-react 192 | ``` 193 | 194 | * Add webpack config file: **webpack.config.js** 195 | 196 | ```js 197 | const HtmlWebPackPlugin = require("html-webpack-plugin"); 198 | 199 | module.exports = { 200 | entry: { 201 | app: "./index.js" 202 | }, 203 | resolve: { 204 | extensions: [".js", ".jsx"], 205 | alias: { 206 | "react-native": "react-native-web" 207 | } 208 | }, 209 | output: { 210 | filename: "bundle.js" 211 | }, 212 | module: { 213 | rules: [ 214 | { 215 | test: /\.js$/, 216 | exclude: /(node_modules|bower_components)/, 217 | use: { 218 | loader: "babel-loader", 219 | options: { 220 | presets: ["@babel/preset-env", "@babel/preset-react"] 221 | } 222 | } 223 | } 224 | ] 225 | }, 226 | plugins: [ 227 | new HtmlWebPackPlugin({ 228 | template: "./web/index.html", 229 | filename: "./index.html" 230 | }) 231 | ], 232 | devServer: { 233 | historyApiFallback: true 234 | } 235 | }; 236 | ``` 237 | 238 | * Add quick start command in **scripts** attribute in package.json 239 | 240 | ```js 241 | { 242 | web: "webpack-dev-server" 243 | } 244 | ``` 245 | 246 | * Update **App.js** 247 | 248 | ```js 249 | // App.js 250 | import React, {Component} from 'react'; 251 | import {Platform, StyleSheet, Text, View} from 'react-native'; 252 | 253 | const instructions = Platform.select({ 254 | ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu', 255 | android: 256 | 'Double tap R on your keyboard to reload,\n' + 257 | 'Shake or press menu button for dev menu', 258 | web: 'Cmd+R or F12 to reload' 259 | }); 260 | 261 | type Props = {}; 262 | export default class App extends Component { 263 | render() { 264 | return ( 265 | 266 | Welcome to React Native! 267 | To get started, edit App.js 268 | {instructions} 269 | 270 | ); 271 | } 272 | } 273 | 274 | const styles = StyleSheet.create({ 275 | container: { 276 | flex: 1, 277 | justifyContent: 'center', 278 | alignItems: 'center', 279 | backgroundColor: '#F5FCFF', 280 | }, 281 | welcome: { 282 | fontSize: 20, 283 | textAlign: 'center', 284 | margin: 10, 285 | }, 286 | instructions: { 287 | textAlign: 'center', 288 | color: '#333333', 289 | marginBottom: 5, 290 | }, 291 | }); 292 | ``` 293 | 294 | * Update **index.js** 295 | 296 | ```js 297 | //index.js 298 | import {AppRegistry, Platform } from 'react-native'; 299 | import App from './App'; 300 | import {name as appName} from './app.json'; 301 | 302 | AppRegistry.registerComponent(appName, () => App); 303 | if (Platform.OS === 'web') { 304 | AppRegistry.runApplication(appName, { 305 | rootTag: document.getElementById("root") 306 | }); 307 | } 308 | ``` 309 | 310 | * Run 311 | 312 | ```sh 313 | npm run web 314 | ``` 315 | ![Web](https://user-images.githubusercontent.com/7504237/59962012-44ef6380-9512-11e9-8d44-723569264d8d.png) 316 | 317 | ## Reference 318 | 319 | * [react-native](https://facebook.github.io/react-native/) 320 | * [react-native-web](https://github.com/necolas/react-native-web) 321 | * [Example Source Code](https://github.com/n0ruSh/rnweb) 322 | 323 | ## Notice 324 | 325 | * If you want to follow the latest news/articles for the series of my blogs, Please [「Watch」](https://github.com/n0ruSh/blogs/)to Subscribe. -------------------------------------------------------------------------------- /articles/about-me/index.mdx: -------------------------------------------------------------------------------- 1 | # 关于我 2 | 3 | # 博客 4 | 5 | # 随想 6 | 7 | - 教育最重要的目标并非提供信息或技术,而是提供一套更有效的视角,去观测那些还没被看到的东西 2022/05 8 | 9 | - 不要做即时反应,即使看到很烦或者很生气的事情,都要像恐龙一样,脚趾那儿疼一下,过了很久这个信息才传达到大脑,感受到疼痛。正在兴头上的时候,即时反应效果会很差 2022/06 10 | 11 | - 撒谎是个中性词。是一种策略。撒谎往往是对某一东西太过看重 2022/06 12 | 13 | - 依赖过往经验决策的领域 (例如医生对症下药, 汽车自动驾驶等),未来是否会被 AI 所替代。 是也不是。是, 因为 AI 具备海量数据的检索, 分析以及不断学习的能力。 不是,因为人们观念的革新(无法完全信任 AI), 以及人需要情感关怀 2022/06 14 | 15 | - 每个个体,都有自己的品牌 2022/06 16 | 17 | # 项目 18 | 19 | [Figma toolbox](https://www.figma.com/community/plugin/1097814681219767573) 20 | 21 | # 社交 22 | 23 | [Github](https://github.com/n0ruSh) 24 | -------------------------------------------------------------------------------- /articles/all-about-my-development/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: All about my development 3 | tags: [development, about] 4 | date: 2022-03-07 5 | --- 6 | 7 | # All about my development 8 | 9 | ## Hardware 10 | 11 | - 21.5-inch iMac, 2017 (Home) 12 | - 16-inch Macbook Pro (Work) 13 | - 11-inch iPad Pro + Apple pencil 14 | - Magic trackpad 15 | - Varmilo keyboard (Blue switch) 16 | 17 | ## Software 18 | 19 | - [Alfred](https://www.alfredapp.com) with workflows: 20 | - [alfred-open-with-vscode](https://github.com/iamstevendao/alfred-open-with-vscode) - Opening a folder with VS Code 21 | - [Localhost](http://www.packal.org/workflow/localhost) - Open localhost at specified port 22 | - [Youdao](https://github.com/whyliam/whyliam.workflows.youdao/) - Youdao translation 23 | - [Github](https://github.com/gharlan/alfred-github-workflow) - Github integration 24 | - [Emoji](https://github.com/carlosgaldino/alfred-emoji-workflow) - This simple workflow lets you search emoji codes and their symbols 25 | - [IP address](https://github.com/zenorocha/alfred-workflows#ip-address-v120--download) - Shows your internal and external IP address 26 | - [Kill process](https://github.com/ngreenstein/alfred-process-killer) - An Alfred workflow that makes it easy to kill misbehaving processes 27 | - [Fig](https://fig.io) - Adds VSCode-style autocomplete to your existing terminal 28 | - [iTerm2](https://iterm2.com/) - A replacement for default macOS's Terminal.app 29 | - [Warp](https://www.warp.dev/) - The terminal for the 21st century 30 | - [Ticktick](https://ticktick.com/) 31 | - [SwitchHosts](https://github.com/oldj/SwitchHosts) 32 | - [pap.er](https://paper.meiyuan.in/) - Elegant wallpaper app for macOS 33 | - [Manico](https://manico.im/) - A fast app launch and switch tool designed for macOS 34 | - [Figma](https://www.figma.com/) - The collaborative interface design tool 35 | - [Xmind](https://xmind.app/) - The full-featured mind mapping and brainstorming app 36 | - [Apple Books](https://www.apple.com/hk/en/apple-books/) - Apple Books is the single destination for all the books you love 37 | - [Excalidraw](https://excalidraw.com/) - A great diagram tool 38 | - [TablePlus](https://tableplus.com/) - Modern, native, and friendly GUI tool for relational databases: 39 | - [VSCode](https://code.visualstudio.com/) with extensions 40 | - [Auto Rename Tag](https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-rename-tag) - Automatically rename paired HTML/XML tag, same as Visual Studio IDE does. Partially supported natively by [VSCode](https://code.visualstudio.com/docs/languages/html#_auto-update-tags) 41 | - [Todo Tree](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.todo-tree) - Searches your workspace for comment tags like TODO and FIXME, and displays them in a tree view in the activity bar 42 | - [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) - A basic spell checker that works well with camelCase code 43 | - [Svg Preview](https://marketplace.visualstudio.com/items?itemName=SimonSiefke.svg-preview) - Live editing of svg files and svg's inside files 44 | - [Template String Converter](https://marketplace.visualstudio.com/items?itemName=meganrogge.template-string-converter) - Converts a string to a template string when "${" is typed. 45 | - [Vim](https://marketplace.visualstudio.com/items?itemName=vscodevim.vim) - Vim emulation for VSCode 46 | - [Import cost](https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost) - Display import/require package size in the editor 47 | - [Gitlens](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) - Supercharges Git inside VSCode 48 | - [Path intellisense](https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense) - Autocompletes filenames 49 | - [Material Icon Theme](https://marketplace.visualstudio.com/items?itemName=PKief.material-icon-theme) - Material design icons for files and folders 50 | - [Polacode-2020](https://marketplace.visualstudio.com/items?itemName=jeff-hykin.polacode-2019) - Polaroid for your code 51 | - [Search node_modules](https://marketplace.visualstudio.com/items?itemName=jasonnutter.search-node-modules) - Quickly search the node_modules 52 | - [Noctis](https://marketplace.visualstudio.com/items?itemName=liviuschera.noctis) - A collection of light & dark themes with a well balanced blend of warm and cold medium contrast colors 53 | - [Rosé Pine](https://marketplace.visualstudio.com/items?itemName=mvllow.rose-pine) - All natural pine, faux fur and a bit of soho vibes for the classy minimalist - vscode theme 54 | - [MDX](https://marketplace.visualstudio.com/items?itemName=unifiedjs.vscode-mdx) - Add language support for MDX 55 | - [MDX Preview](https://marketplace.visualstudio.com/items?itemName=xyc.vscode-mdx-preview) - MDX Preview for Visual Studio Code 56 | - [ESlint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) - Integrates ESlint 57 | - [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) - Integrates Prettier 58 | - [TS in Markdown](https://marketplace.visualstudio.com/items?itemName=amour1688.ts-in-markdown) - Language support for TS in markdown 59 | - [Color highlight](https://marketplace.visualstudio.com/items?itemName=naumovs.color-highlight) - Highlight web colors in your editor 60 | - [Error Lens](https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens): Improve highlighting of errors, warnings and other language diagnostics 61 | - [JavaScript (ES6) code snippets](https://marketplace.visualstudio.com/items?itemName=xabikos.JavaScriptSnippets): VS Code JavaScript (ES6) snippets 62 | - [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) - AI code completion assistant 63 | - [VSCode react refactor](https://marketplace.visualstudio.com/items?itemName=planbcoding.vscode-react-refactor) - Provides JSX refactor code actions for React developers 64 | - [Carbon-now-sh](https://marketplace.visualstudio.com/items?itemName=ericadamski.carbon-now-sh) - A VS Code extension to open the current editor content in carbon.now.sh. 65 | - [Abracadabra](https://marketplace.visualstudio.com/items?itemName=nicoespeon.abracadabra) - Quickly and safely refactor existing code in VS Code 66 | - [Formatting Toggle](https://marketplace.visualstudio.com/items?itemName=tombonnike.vscode-status-bar-format-toggle) - Allows you to toggle your formatting settings ON and OFF with a simple click. 67 | - [Pretty TypeScript Errors](https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors) - Make TypeScript errors prettier and human-readable in VSCode 68 | - ~~[Tabnine](https://marketplace.visualstudio.com/items?itemName=TabNine.tabnine-vscode) - AI code completion assistant~~ 69 | - ~~[Better comments](https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments) - Helps create more human-friendly comments~~ 70 | - ~~[Markdown Preview Enhanced](https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced) - Preview markdown~~ Natively supported by VSCode with `Open Preview` command 71 | - Edge browser with extensions: 72 | - [JSONView](https://microsoftedge.microsoft.com/addons/detail/jsonview/kmpfgkgaimakokfhgdahhiaaiidiphco) - JSON documents are formatted, highlighted, and arrays and objects can be collapsed 73 | - [ModHeader](https://microsoftedge.microsoft.com/addons/detail/modheader/opgbiafapkbbnbnjcdomjaghbckfkglc) - The most popular browser extension to modify headers 74 | - [React Developer tools](https://microsoftedge.microsoft.com/addons/detail/gpphkfbcpidddadnkolkpfckpihlkkil) - A browser DevTools extension for the open-source React JavaScript library 75 | - [Vimium C](https://chrome.google.com/webstore/detail/vimium-c-all-by-keyboard/hfjbmagddngcpeloejdejnfgbamkjaeg) - All by Keyboard 76 | - [Wappalyzer](https://microsoftedge.microsoft.com/addons/detail/wappalyzer-technology-p/mnbndgmknlpdjdnjfmfcdjoegcckoikn) - Uncovers the technologies used on websites 77 | 78 | ## Shell 79 | 80 | - [serve](https://github.com/vercel/serve) - Helps you serve a static site, single page application or just a static file 81 | - [zsh](https://www.zsh.org/) - A Unix shell that is built on top of bash 82 | - [ohmyzsh](https://github.com/ohmyzsh/ohmyzsh) - A delightful framework for managing your zsh configuration 83 | - [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions): Fish-like fast/unobtrusive autosuggestions for zsh 84 | - [zsh-syntax-highlighting](https://github.com/zsh-users/zsh-syntax-highlighting): Fish shell-like syntax highlighting for zsh 85 | - [zsh-history-substring-search](https://github.com/zsh-users/zsh-history-substring-search): History search 86 | - [zsh-vi-mode](https://github.com/jeffreytse/zsh-vi-mode): A better and friendly vi(vim) mode plugin for zsh 87 | - [autojump](https://github.com/wting/autojump): Navigate your filesystem based on learning your preferences 88 | - [copypath](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/copypath): Copies the path of given directory or file to the system clipboard 89 | - [copyfile](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/copyfile): Puts the contents of a file in your system clipboard so you can paste it anywhere 90 | - [colorls](https://github.com/athityakumar/colorls): Beautiful alternative for ls command, with color and font-awesome icons 91 | - [thefuck](https://github.com/nvbn/thefuck): Magnificent app which corrects your previous console command 92 | - [gtop](https://github.com/aksakalli/gtop): System monitoring dashboard for terminal 93 | - [cowsay](https://github.com/piuccio/cowsay): Configurable talking cow 94 | - [figlet](http://www.figlet.org/): A program for making large letters out of ordinary text 95 | - [lolcat](https://github.com/busyloop/lolcat): Replacement for cat with colorful output 96 | - [boxes](https://github.com/ascii-boxes/boxes): Command line ASCII boxes unlimited 97 | - [tldr](https://github.com/tldr-pages/tldr): A collection of community-maintained help pages for command-line tools 98 | - [scc](https://github.com/boyter/scc): Counting physical the lines of code, blank lines, comment lines, and physical lines of source code 99 | - [exa](https://github.com/ogham/exa): A modern replacement for the venerable file-listing command-line program `ls` 100 | 101 | ### Utilities 102 | 103 | - [Carbon](https://carbon.now.sh/) - Create and share beautiful images of your source code 104 | - [OnlineConverter](https://www.onlineconverter.com/video-to-gif) - Convert video to gif 105 | - [Unsplash](https://unsplash.com/) - Beautiful free images & pictures 106 | - [Excalidraw](https://excalidraw.com/) - Virtual collaborative whiteboard tool that lets you easily sketch diagrams that have a hand-drawn feel 107 | - [XClient](https://xclient.info/s/) - Xclient for apps 108 | - [Roadmap](https://roadmap.sh/) - Developer roadmap 109 | -------------------------------------------------------------------------------- /articles/git-config/index.md: -------------------------------------------------------------------------------- 1 | [branch] 2 | sort = -committerdate 3 | [tag] 4 | sort = version:refname 5 | [diff] 6 | algorithm = histogram 7 | colorMoved = plain 8 | mnemonicPrefix = true 9 | renames = true 10 | [push] 11 | default = simple 12 | autoSetupRemote = true 13 | followTags = true 14 | [init] 15 | defaultBranch = main 16 | [pull] 17 | rebase = true 18 | [core] 19 | editor = vim 20 | [user] 21 | name = headwindz 22 | email = zhengjianhua.michael@gmail.com 23 | [commit] 24 | verbose = true 25 | [merge] 26 | conflictstyle = zdiff3 27 | -------------------------------------------------------------------------------- /articles/my-ts-cheat-sheet/index.mdx: -------------------------------------------------------------------------------- 1 | ## keyof 2 | 3 | The `keyof` operator takes an object type and produces a string or numeric literal union of its keys. 4 | 5 | For example, it can be used to create a more readable and maintainable type for an object: 6 | 7 | ```ts 8 | interface IUser { 9 | name: string; 10 | age: number; 11 | } 12 | 13 | type UserKeys = keyof IUser; // "name" | "age" 14 | ``` 15 | 16 | Use the `keyof` operator to create more type-safe functions 17 | 18 | ```ts 19 | function getValue(obj: T, key: K) { 20 | return obj[key]; 21 | } 22 | let user: User = { name: "Mike", age: 18 }; 23 | getValue(user, "name"); // John 24 | getValue(user, "gender"); // Error thrown 25 | ``` 26 | 27 | ## Exclude 28 | 29 | `Exclude` utility type can be used to remove properties from an object type 30 | 31 | ```ts 32 | interface IUser { 33 | name: string; 34 | age: number; 35 | } 36 | type UserWithoutAge = Exclude; 37 | 38 | type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c" 39 | ``` 40 | 41 | Official `Exclude` implementation 42 | 43 | ```ts 44 | type Exclude = T extends U ? never : T; 45 | ``` 46 | 47 | ## ReturnType 48 | 49 | Grab the type returned from a function 50 | 51 | ```ts 52 | const sum = (a: number, b: number) => a + b; 53 | 54 | type SumResult = ReturnType; // number 55 | ``` 56 | 57 | ## Await 58 | 59 | `Awaited` type can be used unwrap the promise and get the type of what the promise resolves to 60 | 61 | ```ts 62 | const sum = (a: number, b: number) => a + b; 63 | 64 | type SumResult = Awaited>; // number 65 | ``` 66 | 67 | ## Generic 68 | 69 | ```ts 70 | function toLabelValue( 71 | labelKey: LabelKey, 72 | valueKey: ValueKey, 73 | obj: T 74 | ) { 75 | return { 76 | label: obj[labelKey], 77 | value: obj[valueKey], 78 | }; 79 | } 80 | 81 | interface Lib { 82 | id: number; 83 | name: string; 84 | isPublic: boolean; 85 | } 86 | 87 | const lib = { id: 1, name: "hi", isPublic: true }; 88 | 89 | // 让 labelValue 类型是 { label: string, value: number } 90 | const labelValue = toLabelValue("name", "id", lib); 91 | ``` 92 | -------------------------------------------------------------------------------- /articles/my-vim-cheat-sheet/index.mdx: -------------------------------------------------------------------------------- 1 | # My VIM Cheat Sheet 2 | 3 | ## Movement 4 | 5 | - `gg`: the beginning of the file 6 | - `G`: the end of the file 7 | - `0`: the beginning of the line 8 | - `^`: the first non-blank character of the line 9 | - `$`: the end of the line 10 | - `w`: next word 11 | - `b`: previous word 12 | - `e`: end of word 13 | 14 | ## Insert -> insert mode 15 | 16 | - `I`: insert at the beginning of the line 17 | - `A`: insert at the end of the line 18 | - `o`: insert a new line below the current line 19 | - `O`: insert a new line above the current line 20 | 21 | ## Actions 22 | 23 | - `d`: delete 24 | - `c`: change 25 | - `y`: copy 26 | - `v`: enter visual mode 27 | 28 | ## Motions 29 | 30 | - `i{object}`: inner object(inside) 31 | - `a{object}`: outer object(around) 32 | 33 | ## Text objects 34 | 35 | - `w`: word 36 | - `b`: bracket (or `(`, `{`, `[`, `<`) 37 | - `t`: tag 38 | - `q`: quote (or `'`, `"`, ```) 39 | 40 | ## Return to normal mode 41 | 42 | - Esc 43 | - Ctrl + [ 44 | 45 | ## Miscellaneous 46 | 47 | - `gd`: go to definition 48 | - `f`: find character 49 | 50 | ### Move to other end of marked area Visual mode 51 | 52 | Press `o` at visual mode 53 | 54 | ### Change case in Vim 55 | 56 | Select the text then: 57 | 58 | - `U` for uppercase 59 | - `u` for lowercase 60 | - `~` for toggle case 61 | -------------------------------------------------------------------------------- /articles/resources/index.mdx: -------------------------------------------------------------------------------- 1 | ## Learning 2 | 3 | ### CSS 4 | 5 | - [CSS Secrets](https://www.oreilly.com/library/view/css-secrets/9781098124373/) 6 | - [CSS in Depth](https://www.oreilly.com/library/view/css-in-depth/9781617293450/) 7 | 8 | ### JavaScript 9 | 10 | - [JavaScript: The Definitive Guide](https://www.oreilly.com/library/view/javascript-the-definitive/9781491952016/) 11 | - [Professional JavaScript for Web Developers](https://www.oreilly.com/library/view/professional-javascript-for/9781119366447/) 12 | - [You Don't Know JS Series](https://www.oreilly.com/library/view/you-dont-know/9781491905159) 13 | - [JavaScript: The Good Parts](https://www.oreilly.com/library/view/javascript-the-good/9780596517748) 14 | 15 | ### Http 16 | 17 | - [HTTP: The Definitive Guide](https://www.oreilly.com/library/view/http-the-definitive/1565925092/) 18 | - [图解 http](https://item.jd.com/12837057.html) 19 | - [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP) 20 | 21 | ### Performance 22 | 23 | - [Even Faster Web Sites](https://www.oreilly.com/library/view/even-faster-web/9780596803773/) 24 | - [High Performance Web Sites](https://www.oreilly.com/library/view/high-performance-web/9780596529307/) 25 | 26 | ## Blogs 27 | 28 | - [kentcdodds](https://kentcdodds.com/) - Kent C. Dodds 29 | - [overreacted](https://overreacted.io/) - Dan Abramov 30 | - [igor.dev](https://igor.dev/) - Igor Minar 31 | - [joshwcomeau.com](https://www.joshwcomeau.com/) - Josh W Comeau 32 | - [leerob](https://leerob.io/) - Lee Robinson 33 | - [jakelazaroff](https://jakelazaroff.com/) - Jake Lazaroff 34 | - [robin wieruch](https://www.robinwieruch.de/) - ROBIN WIERUCH 35 | - [Builder.io](https://www.builder.io/m/subscribe-newsletter) - Builder.io 36 | 37 | ## Newsletter subscriptions 38 | 39 | ### Tech 40 | 41 | - [Hashnode](https://hashnode.com/) - Blogs 42 | - [Dev.to](https://dev.to/) - Blogs 43 | - [Medium](https://medium.com/) - Blogs 44 | - [This Week In React](https://thisweekinreact.com/) 45 | - [Javascriptweekly](https://javascriptweekly.com/) 46 | - [Frontend Weekly](https://medium.com/front-end-weekly) 47 | - [Frontend Focus](https://frontendfoc.us/) 48 | - [Frontend Mastery](https://frontendmastery.com) 49 | - [Remix](https://remix.run/blog) 50 | - [Bytes](https://bytes.dev/) - JavaScript ecosystem news 51 | - [Get better at software architecture](https://www.codephilosophy.dev/) - A newsletter about architecture & design. Focused on JavaScript 52 | 53 | ### Influencer 54 | 55 | - [Mark Manson](https://markmanson.net/) - 5 Minutes That Might Change Your Life 56 | - [Darius Foboux](https://dariusforoux.com/) - The Wise & Wealthy newsletter: Receive weekly ideas to become smarter and wealthier 57 | - [Addy Osmani](https://addyo.substack.com/) 58 | - [Flavienbonvin](https://www.flavienbonvin.com/) 59 | 60 | ### AI 61 | 62 | - [The AI Solopreneur](https://aisolopreneur.beehiiv.com/) - Solopreneurs to get actionable insights on AI workflows, hacks and tactics to help your business grow 63 | - [AI Valley](https://www.theaivalley.com/) - AI news 64 | - [Superhuman](https://www.joinsuperhuman.ai/) - Learn how to leverage AI to boost your productivity and accelerate your career 65 | - [DeepLearning.AI](https://www.deeplearning.ai/) - Build your AI career with DeepLearning.AI 66 | 67 | ### Finance 68 | 69 | - [Bay Area Times](https://www.bayareatimes.com/) - The visual daily newsletter on business and tech 70 | - [McKinsey](https://www.mckinsey.com/) - Business insights 71 | - [The Average Joe](https://readthejoe.com/) - Market insights, trends and analysis to help you become a better investor. 72 | 73 | ### Others 74 | 75 | - [Smashing Magazine](https://www.smashingmagazine.com/) 76 | - [Builtin](https://builtin.com/) - Remote oppotunities + tech news 77 | - [Ui.dev](https://ui.dev/) 78 | - [Refined](https://refind.com/) - Daily curated list of articles in various fields 79 | - [Prequel](https://prequel.beehiiv.com/) - The newsletter for parents who want their kids to succeed 80 | - [Turtle's Pace](https://turtlespace.blog/about?utm_source=subscribe_email&utm_content=learn_more) - A newsletter about making things that matter at a sustainable pace. 81 | - [Fronter](https://fronterablog.com/life-changing-concepts-newsletter/) - Discover the cheat codes of business 82 | - [DevTools Brew](https://morganperry.substack.com/) - The stories, strategies, and insights behind the most successful devtool companies 83 | - [TED](https://www.ted.com/newsletters) - Ideas worth spreading 84 | - [Dense Discovery](https://www.densediscovery.com/) - The best of the internet, thoughtfully curated 85 | - [Thoughtworks Technology Radar](https://www.thoughtworks.com/radar) - An opinionated guide to today's technology landscape 86 | - [Alex & Books](https://alexandbooks.beehiiv.com/) - Become smarter, happier, and wiser with 5-minute book summaries -------------------------------------------------------------------------------- /images/rn-android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headwindz/blogs/7bc70b02fcba13b683c39e7683981bf0bdb626c6/images/rn-android.png -------------------------------------------------------------------------------- /images/rn-ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headwindz/blogs/7bc70b02fcba13b683c39e7683981bf0bdb626c6/images/rn-ios.png -------------------------------------------------------------------------------- /images/rn-structure.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headwindz/blogs/7bc70b02fcba13b683c39e7683981bf0bdb626c6/images/rn-structure.jpg -------------------------------------------------------------------------------- /images/rn-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headwindz/blogs/7bc70b02fcba13b683c39e7683981bf0bdb626c6/images/rn-web.png -------------------------------------------------------------------------------- /images/tedder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/headwindz/blogs/7bc70b02fcba13b683c39e7683981bf0bdb626c6/images/tedder.png --------------------------------------------------------------------------------