Semanticize intermediate headings
This commit is contained in:
@ -9,7 +9,7 @@ excerpt: JavaScript arrays have a very robust API offering a plethora of amazing
|
||||
|
||||
JavaScript arrays have a very robust API offering a plethora of amazing tools. Here are our top 4 JavaScript array methods every developer should know:
|
||||
|
||||
**Array.prototype.map()**
|
||||
### Array.prototype.map()
|
||||
|
||||
[`Array.prototype.map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) creates a new array by applying the provided transformation to each element of the original array. The result is an array with the same length as the original array and elements transformed based on the provided function.
|
||||
|
||||
@ -19,7 +19,7 @@ const double = x => x * 2;
|
||||
arr.map(double); // [2, 4, 6]
|
||||
```
|
||||
|
||||
**Array.prototype.filter()**
|
||||
### Array.prototype.filter()
|
||||
|
||||
[`Array.prototype.filter()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) creates a new array by using a filtering function to keep only elements that return `true` based on that function. The result is an array with equal or less than the original array's length, containing a subset of the same elements as the original array.
|
||||
|
||||
@ -31,7 +31,7 @@ arr.filter(isOdd); // [1, 3]
|
||||
|
||||

|
||||
|
||||
**Array.prototype.reduce()**
|
||||
### Array.prototype.reduce()
|
||||
|
||||
[`Array.prototype.reduce()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) creates an output value of any type depending on a reducer function and an initial value. The result can be of any type such as an integer, an object or an array, based on the reducer function provided.
|
||||
|
||||
@ -45,7 +45,7 @@ const increment = (x, y) => [...x, x[x.length - 1] + y];
|
||||
arr.reduce(increment, [0]); // [0, 1, 3, 6]
|
||||
```
|
||||
|
||||
**Array.prototype.find()**
|
||||
### Array.prototype.find()
|
||||
|
||||
[`Array.prototype.find()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) returns the first element for which a matcher function returns `true`. The result is a single element from the original array.
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ excerpt: Regular expressions, while very powerful, are notoriously hard to maste
|
||||
|
||||
Regular expressions, while very powerful, are notoriously hard to master. Here are 6 useful features that can help you start using them in your JavaScript projects:
|
||||
|
||||
**Capturing groups**
|
||||
### Capturing groups
|
||||
|
||||
Capturing groups allow you to get specific parts of the matched string, simply by wrapping part of the regular expression in parentheses `(...)`:
|
||||
|
||||
@ -25,7 +25,7 @@ const str = 'JavaScript is a programming language';
|
||||
*/
|
||||
```
|
||||
|
||||
**Non-capturing groups**
|
||||
### Non-capturing groups
|
||||
|
||||
Non-capturing groups are used for matching something without capturing it, like an either/or matching group that you do not really need. They are defined similarly to capturing groups, but prefixed with `?:`:
|
||||
|
||||
@ -40,7 +40,7 @@ const str = 'JavaScript is a programming language';
|
||||
*/
|
||||
```
|
||||
|
||||
**Named capturing groups**
|
||||
### Named capturing groups
|
||||
|
||||
Named capturing groups allow you to name a capturing group, by prefixing it with `<name>`:
|
||||
|
||||
@ -60,7 +60,7 @@ const str = 'JavaScript is a programming language';
|
||||
*/
|
||||
```
|
||||
|
||||
**Capturing group backreferences**
|
||||
### Capturing group backreferences
|
||||
|
||||
Backreferences help you write shorter regular expressions, by repeating an existing capturing group, using `\1`, `\2` etc. Similarly, you can also repeat named capturing groups using `\k<name>`:
|
||||
|
||||
@ -80,7 +80,7 @@ const str = 'JavaScript is a programming language - an awesome programming langu
|
||||
*/
|
||||
```
|
||||
|
||||
**Lookaheads**
|
||||
### Lookaheads
|
||||
|
||||
Lookaheads allow you to check if something is followed by a certain pattern, without actually matching it. You can create positive lookaheads using `?=` and negative lookaheads using `?!`:
|
||||
|
||||
@ -103,7 +103,7 @@ const str = 'JavaScript is not the same as Java and you should remember that';
|
||||
*/
|
||||
```
|
||||
|
||||
**Unicode characters**
|
||||
### Unicode characters
|
||||
|
||||
Finally, you can match unicode characters, using `/p{...}` and the `/u` flag. Examples include, but are not limited to `{Emoji}`, `{Math_Symbols}` and `{Script=Greek}`:
|
||||
|
||||
|
||||
@ -7,13 +7,13 @@ cover: blog_images/green-plant.jpg
|
||||
excerpt: Learn everything you need to know about promises and asynchronous JavaScript with this handy cheatsheet.
|
||||
---
|
||||
|
||||
**Promise basics**
|
||||
### Promise basics
|
||||
|
||||
- **Promises** start in a **pending state**, neither fullfiled or rejected.
|
||||
- When the operation is completed, a promise will become **fullfiled with a value**.
|
||||
- If the operation fails, a promise will get **rejected with an error**.
|
||||
|
||||
**Creating promises**
|
||||
### Creating promises
|
||||
|
||||
- The function passed to a `new Promise` will execute synchronously.
|
||||
- Use `resolve()` or `reject()` to create promises from values.
|
||||
@ -34,7 +34,7 @@ new Promise((resolve, reject) => {
|
||||
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
```
|
||||
|
||||
**Handling promises**
|
||||
### Handling promises
|
||||
|
||||
- `Promise.prototype.then()` accepts two optional arguments (`onFulfilled`, `onRejected`).
|
||||
- `Promise.prototype.then()` will call `onFulfilled` once the promise is fulfilled.
|
||||
@ -68,7 +68,7 @@ promisedOperation()
|
||||
|
||||
- All three of the above methods will not be executed at least until the next tick, even for promises that already have an outcome.
|
||||
|
||||
**Combining promises**
|
||||
### Combining promises
|
||||
|
||||
- `Promise.all()` turns an array of promises into a promise of an array.
|
||||
- If any promise is rejected, the error will pass through.
|
||||
@ -89,7 +89,7 @@ Promise
|
||||
});
|
||||
```
|
||||
|
||||
**async/await**
|
||||
### async/await
|
||||
|
||||
- Calling an `async` function always results in a promise.
|
||||
- `(async () => value)()` will resolve to `value`.
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/code-anatomy-optimizing-recursion.jpg
|
||||
excerpt: There are many ways to iterate and transform array data in JavaScript. Learn how each one works and where you should use them.
|
||||
---
|
||||
|
||||
**For loops**
|
||||
### For loops
|
||||
|
||||
```js
|
||||
const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ];
|
||||
@ -31,7 +31,7 @@ for (let file of files) {
|
||||
- Uses `Array.prototype.push()` or the spread (`...`) operator to add elements.
|
||||
- `O(N)` complexity, each element will be iterated over only once.
|
||||
|
||||
**Array reduce**
|
||||
### Array reduce
|
||||
|
||||
```js
|
||||
const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ];
|
||||
@ -54,7 +54,7 @@ const filePaths = files.reduce((acc, file) => {
|
||||
- Uses `Array.prototype.push()` or the spread (`...`) operator to add elements.
|
||||
- `O(N)` complexity, each element will be iterated over only once.
|
||||
|
||||
**Method chaining**
|
||||
### Method chaining
|
||||
|
||||
```js
|
||||
const files = [ 'foo.txt ', '.bar', ' ', 'baz.foo' ];
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/code-anatomy-optimizing-recursion.jpg
|
||||
excerpt: Recursive code has a tendency of being inefficient and can leave a lot of space for optimization. Learn a couple of tricks we use to speed up our recursive functions.
|
||||
---
|
||||
|
||||
**Recursive functions**
|
||||
### Recursive functions
|
||||
|
||||
Recursion is a programming technique where the final solution is computed by breaking down the problem into smaller instances of the same problem and computing the solution for each one. The most common implementation is a function that calls itself, reducing the problem every time until it reaches an instance of the problem whose solution is either trivial to compute or already known. Let's look at a very well-known example, calculating the `n`th term of the [Fibonacci sequence](https://en.wikipedia.org/wiki/Fibonacci_number), implemented using recursion in JavaScript:
|
||||
|
||||
@ -49,7 +49,7 @@ fibonacciNumber(4);
|
||||
|
||||
As you can see, for each value of `n`, `fibonacciNumber` will be called twice, once with `n - 1` and once with `n - 2` and this will continue until it's called with either `1` or `0`. While this is straightforward to write and understand, it is inefficient as it will have to calculate the same value more than once.
|
||||
|
||||
**Calculation memoization**
|
||||
### Calculation memoization
|
||||
|
||||
The solution to this problem, and the first trick that you can use to speed up recursive functions, is to use memoization. We already published [a great blog post on memoization](https://www.30secondsofcode.org/blog/s/javascript-memoization/) a little while back, so be sure to check it out to learn more about the subject. Here's our `fibonacciNumber` function, using memoization:
|
||||
|
||||
@ -91,7 +91,7 @@ fibonacciNumber(4);
|
||||
|
||||
As you can see in the example above, the value for each `n` is only computed once. While the Fibonacci sequence doesn't require any costly calculations, this could make a huge difference for a more computationally expensive problem. It will also be a lot more noticable for higher values of `n` where the number of calculations will increase singificantly.
|
||||
|
||||
**Using iteration**
|
||||
### Using iteration
|
||||
|
||||
The second and final trick stems from the very definition of recursive programming turned on its head. If we can solve a smaller instance of the problem and use it for the solution of a larger instance of the problem, it should be possible to work iteratively from the smaller problem to the larger one, instead of recursively. Here's this idea in practice for our `fibonacciNumber` function:
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/three-vases.jpg
|
||||
excerpt: Learn the difference between cookies, local storage and session storage and start using the correct option for your needs.
|
||||
---
|
||||
|
||||
**Cookies**
|
||||
### Cookies
|
||||
|
||||
Cookies store small amounts of data that has to be sent back to the server with subsequent requests and their expiration can be set from either server or client. They are primarily used for server-side reading.
|
||||
|
||||
@ -19,7 +19,7 @@ Cookies store small amounts of data that has to be sent back to the server with
|
||||
- Blockable by users: Yes
|
||||
- Editable by users: Yes
|
||||
|
||||
**Local storage**
|
||||
### Local storage
|
||||
|
||||
Local storage stores a larger amount of data on the client's computer in a key-value pair format and has no expiration date. Data is never transferred to the server and is accesible via JavaScript and HTML5.
|
||||
|
||||
@ -31,7 +31,7 @@ Local storage stores a larger amount of data on the client's computer in a key-v
|
||||
- Blockable by users: Yes
|
||||
- Editable by users: Yes
|
||||
|
||||
**Session storage**
|
||||
### Session storage
|
||||
|
||||
Session storage stores a larger amount of data on the client's computer only for the current session, expiring the data on tab close. Data is never transferred to the server and is accessible client-side from the same tab.
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/copy-text-to-clipboard-with-javascript.jpg
|
||||
excerpt: Learn how to programmatically copy text to clipboard with a few lines of JavaScript and level up your web development skills.
|
||||
---
|
||||
|
||||
**Core functionality**
|
||||
### Core functionality
|
||||
|
||||
A very common need when building websites is the ability to copy text to clipboard with a single button click. Javascript can easily do this in five short steps:hout the user selecting it or hitting the appropriate key combination on their keyboard. Javascript can easily do this in five short steps:
|
||||
|
||||
@ -32,7 +32,7 @@ const copyToClipboard = str => {
|
||||
|
||||
Bear in mind that this method will not work everywhere, but only as a result of a user action (e.g. inside a `click` event listener), due to the way `Document.execCommand()` works.
|
||||
|
||||
**Hide the appended element**
|
||||
### Hide the appended element
|
||||
|
||||
The above method, while functional, might have some issues such as flashing when appending and removing the `<textarea>`, a problem that is even more apparent when considering accessibility. A major improvement to this method comes from adding some CSS to make the element invisible and restrict editing by users:
|
||||
|
||||
@ -50,7 +50,7 @@ const copyToClipboard = str => {
|
||||
};
|
||||
```
|
||||
|
||||
**Save and restore the original document's selection**
|
||||
### Save and restore the original document's selection
|
||||
|
||||
The final consideration before wrapping this up is respecting the user's previous interaction with the website, like having already selected some content. Luckily, we can now use some modern Javascript methods and properties like `DocumentOrShadowRoot.getSelection()`, `Selection.rangeCount`, `Selection.getRangeAt()`, `Selection.removeAllRanges()` and `Selection.addRange()` to save and restore the original document selection. You can find the final code with these improvements implemented in the [copyToClipboard snippet](/js/s/copy-to-clipboard/).
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ CSS pseudo-classes provide a way to style elements, based on changes to their st
|
||||
|
||||
Pseudo-classes let you apply styles to elements in relation to the content of the document tree (e.g. `:first-child`), external factors such as the history of the navigator (e.g. `:visited`), the status of their content (e.g. `:checked`) or the position of the mouse (e.g. `:hover`).
|
||||
|
||||
**Commonly used pseudo-classes**
|
||||
### Commonly used pseudo-classes
|
||||
|
||||
Below is a list of the top 5 most commonly used pseudo-classes and their usage. This list is by no means complete; you should always refer to relevant documentation from authoritative sources, such as [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes) for more information.
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ ESLint is one of my tools of choice, but oftentimes it gets in the way of work,
|
||||
|
||||
And here are three refactoring options to deal with it:
|
||||
|
||||
**Object.keys()**
|
||||
### Object.keys()
|
||||
|
||||
`Object.keys()` has the exact same behavior as a `for..in` loop, so it can be used as a drop-in replacement:
|
||||
|
||||
@ -24,7 +24,7 @@ Object.keys(data).forEach(k => console.log(k));
|
||||
// 0 1
|
||||
```
|
||||
|
||||
**Object.values()**
|
||||
### Object.values()
|
||||
|
||||
`Object.values()` is very similar to `Object.keys()`, but returns the values instead of the keys, which might be what you are really using the keys for:
|
||||
|
||||
@ -35,7 +35,7 @@ Object.keys(data).forEach(v => console.log(v));
|
||||
// 3 4
|
||||
```
|
||||
|
||||
**Object.entries()**
|
||||
### Object.entries()
|
||||
|
||||
Finally, if you need both key and value, `Object.entries()` has you covered:
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/flexbox.jpg
|
||||
excerpt: Flexbox allows you to create fluid layouts easily. If you find yourself constantly looking up the syntax or how it work, this handy cheatsheet is all you need.
|
||||
---
|
||||
|
||||
**Container**
|
||||
### Container
|
||||
|
||||
- `display: flex` or `display: inline-flex`: creates a flex context (or an inline flex context) for direct children of this element
|
||||
- `flex-direction` determines the main and cross axis for the container, valid values are:
|
||||
@ -54,7 +54,7 @@ excerpt: Flexbox allows you to create fluid layouts easily. If you find yourself
|
||||
|
||||

|
||||
|
||||
**Items**
|
||||
### Items
|
||||
|
||||
- `flex-grow` determines how much the item can grow if necessary
|
||||
- Accepts a single positive number (unitless), default value is `0`
|
||||
|
||||
@ -17,7 +17,7 @@ Co-authored-by: name <name@example.com>
|
||||
Co-authored-by: another-name <another-name@example.com>"
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
### Notes:
|
||||
|
||||
- To correctly attribute a commit to a co-author, you must use the email associated with their GitHub account.
|
||||
- If a person's email is private, you can use their GitHub-provided `no-reply` email.
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/coconuts.jpg
|
||||
excerpt: Learn how you can compare two arrays in JavaScript using various different techniques.
|
||||
---
|
||||
|
||||
**Equality comparison**
|
||||
### Equality comparison
|
||||
|
||||
Comparing two arrays in JavaScript using either the loose or strict equality operators (`==` or `===`) will most often result in `false`, even if the two arrays contain the same elements in the same order. This is due to the fact that arrays and objects are compared by reference and not by value in JavaScript, which means this solution does not produce the desired result:
|
||||
|
||||
@ -18,7 +18,7 @@ const b = [1, 2, 3];
|
||||
a === b; // false
|
||||
```
|
||||
|
||||
**JSON.stringify**
|
||||
### JSON.stringify
|
||||
|
||||
A common solution that many people suggest is to use `JSON.stringify()`, which allows us to serialize each array and then compare the two serialized strings. A simple implementation of this might look something like this:
|
||||
|
||||
@ -45,7 +45,7 @@ equals([null], [undefined]); // true, should be false
|
||||
|
||||
While these cases seem rather uncommon, they might cause some very annoying issues that are hard to track down and fix, which is why this solution is not recommended for most use-cases.
|
||||
|
||||
**A better way**
|
||||
### A better way
|
||||
|
||||
A better approach would be to compare the two arrays' `length`s and use `Array.prototype.every()` to compare the values of the two:
|
||||
|
||||
@ -66,7 +66,7 @@ equals([null], [undefined]); // false
|
||||
|
||||
This approach safeguards against the serialization issue described above, however it does not take into account nested arrays or objects, which need to be checked recursively. For a robust solution that handles this and other issues, you should use the [equals snippet](/js/s/equals).
|
||||
|
||||
**Comparing out of order**
|
||||
### Comparing out of order
|
||||
|
||||
Finally, there are cases where the order of the elements in each array is not important and we only care about the same values existing in both arrays. For these cases, you can use `Set` and `Array.prototype.filter()` in combination with a loop to iterate over unique values and check if each one appears the same amount of times in each array:
|
||||
|
||||
|
||||
@ -7,13 +7,13 @@ cover: blog_images/arrow-functions.jpg
|
||||
excerpt: Learn the differences between JavaScript ES6 arrow functions and regular functions and how they affect event listener callbacks.
|
||||
---
|
||||
|
||||
**Arrow functions**
|
||||
### Arrow functions
|
||||
|
||||
JavaScript ES6 introduced the concept of arrow functions, a new way to define and write functions. While they might seem like a syntactic sugar on top of regular functions, they have a key difference which lies in the way the `this` context is bound. I will not go into a lot of detail in this article, however I strongly suggest you read [Understanding the "this" keyword in JavaScript](/blog/s/javascript-this) before continuing. To summarize what the afforementioned blog post explains in more detail:
|
||||
|
||||
> Arrow functions do not have their own bindings for `this`, resulting in `this` retaining the value of the enclosing lexical context's `this`.
|
||||
|
||||
**Event listener callbacks**
|
||||
### Event listener callbacks
|
||||
|
||||
One task that we often perform when writing browser-side JavaScript is creating event listeners. For example:
|
||||
|
||||
@ -28,7 +28,7 @@ toggleElements.forEach(el => {
|
||||
|
||||
In the example above, we use `NodeList.prototype.forEach()` to iterate over the nodes matching a given selector and `EventTarget.addEventListener()` with a regular function as the callback for the `'click'` event to swap between an active and inactive state for the clicked element. As we are using a regular function, the `this` context inside the callback will be bound to the element on which the event was fired.
|
||||
|
||||
**Arrow functions as callbacks**
|
||||
### Arrow functions as callbacks
|
||||
|
||||
As we have already explained, arrow functions do not have their own bindings for `this`. So what happens if we convert the previous code snippet's callback to an arrow function? Its `this` context refers to the global one, which in this case is the `window` object.
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ excerpt: JavaScript's built-in Boolean function can be very useful for truth-che
|
||||
|
||||
JavaScript's built-in [`Boolean`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean) is one of those things I find myself suggesting in code reviews quite often as of late, so I thought I could share some tips about it with the world.
|
||||
|
||||
**Using Boolean for truth-checking**
|
||||
### Using Boolean for truth-checking
|
||||
|
||||
The `Boolean()` function is particularly useful when truth-checking data and probably significantly more readable than the double negation (`!!`) operation:
|
||||
|
||||
@ -37,7 +37,7 @@ const hasValidValue = values.some(Boolean);
|
||||
const nonEmptyValues = values.filter(Boolean);
|
||||
```
|
||||
|
||||
**Handle Boolean objects with care**
|
||||
### Handle Boolean objects with care
|
||||
|
||||
While the `Boolean()` function is pretty useful, you might run into some issues with the `Boolean` object and the `new Boolean()` constructor. The `Boolean` object is an object wrapper for a boolean value, but the tricky part is that, as an object, it's always truthy even if the contained value is `false`!
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/js-event-capture.jpg
|
||||
excerpt: Understand how events work in JavaScript and learn when to use event bubbling, event capturing and event delegation with this short guide.
|
||||
---
|
||||
|
||||
**Event bubbling**
|
||||
### Event bubbling
|
||||
|
||||
Bubbling means that the event propagates from the target element (i.e. the `button` the user clicked) up through its ancestor tree, starting from the nearest one. By default, all events bubble.
|
||||
|
||||
@ -43,7 +43,7 @@ ancestors.forEach(a => {
|
||||
|
||||
If we add an event listener to each element in the tree, as shown above, we would see a listener fired by the `button` first, then each one of the others firing from the nearest ancestor all the way up to `window`.
|
||||
|
||||
**Event capturing**
|
||||
### Event capturing
|
||||
|
||||
Capturing is the exact opposite of bubbling, meaning that the outer event handlers are fired before the most specific handler (i.e. the one on the `button`). Note that all capturing event handlers are run first, then all the bubbling event handlers.
|
||||
|
||||
@ -60,7 +60,7 @@ ancestors.forEach(a => {
|
||||
|
||||
Given this code, we would see a listener fired for each ancestor of the `button` first and then the listener of the `button` would fire.
|
||||
|
||||
**Event propagation**
|
||||
### Event propagation
|
||||
|
||||
Having explained event bubbling and capturing, we can now explain the three phases of event propagation:
|
||||
|
||||
@ -68,7 +68,7 @@ Having explained event bubbling and capturing, we can now explain the three phas
|
||||
- During the **target phase**, the event gets triggered on the event target (e.g. the `button` the user clicked).
|
||||
- During the **bubble phase**, the event bubbles up through ancestors of the target element until the root element, `document` and, finally, `window`.
|
||||
|
||||
**Event delegation**
|
||||
### Event delegation
|
||||
|
||||
Event delegation refers to the idea of delegating event listening to parent elements instead of adding event listeners directly to the event targets. Using this technique, the parent can catch and handle the bubbling events as necessary.
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ JavaScript ES6 introduced, among many other things, the [spread operator (`...`)
|
||||
|
||||
We can use the spread operator to convert iterables or, as they are sometimes referred to, array-likes. Let's take a look at some examples:
|
||||
|
||||
**String**
|
||||
### String
|
||||
|
||||
When the spread operator is applied to a string, the result is an array of strings each one representing a character of the original string:
|
||||
|
||||
@ -20,7 +20,7 @@ const name = 'Zelda';
|
||||
const letters = [...name]; // 'Z', 'e', 'l', 'd', 'a'
|
||||
```
|
||||
|
||||
**Set**
|
||||
### Set
|
||||
|
||||
A [`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) is a collection of unique values. When the spread operator is applied to it, the result is an array of the stored values:
|
||||
|
||||
@ -32,7 +32,7 @@ const uniqueValues = [...values]; // [1, 2, 3, 4]
|
||||
|
||||
Note that the above example is the basis for the [uniqueElements snippet](/js/s/unique-elements).
|
||||
|
||||
**NodeList**
|
||||
### NodeList
|
||||
|
||||
A [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList) is a collection of nodes, returned by methods such as `document.childNodes()` or `document.querySelectorAll()`. While it implements some methods that help manipulate it as an array (e.g. `NodeList.prototype.forEach()`), it's oftentimes desirable to convert it to an array. When the spread operator is applied to it, the result is an array of the contained nodes:
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/javascript-listen-once.jpg
|
||||
excerpt: Learn how to attach an event handler to events that is executed at most once in this JavaScript blog post.
|
||||
---
|
||||
|
||||
**jQuery**
|
||||
### jQuery
|
||||
|
||||
Back in the day when jQuery was all the rage, we would usually use [`$.one()`](https://api.jquery.com/one/) to create an event handler that would execute at most once for a given event per element. A simple example would be as follows:
|
||||
|
||||
@ -21,7 +21,7 @@ $('#my-btn').one('click', () => {
|
||||
});
|
||||
```
|
||||
|
||||
**Using a flag**
|
||||
### Using a flag
|
||||
|
||||
However, jQuery seems to have fallen out of favor lately and thus many developers have resorted to writing their version of `$.one()`. An implementation could look like this:
|
||||
|
||||
@ -43,7 +43,7 @@ listenOnce(
|
||||
|
||||
In this implementation, we use a flag, `fired`, to check if the event has been triggered before and only execute the passed callback, `fn`, the first time the event is triggered. There are some details that we might have omitted such as removing the listener, but overall this is a reasonably solid implementation.
|
||||
|
||||
**Event listener options**
|
||||
### Event listener options
|
||||
|
||||
If you are targeting modern browsers (i.e. not IE), [`EventTarget.addEventListener()`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) has introduced the `options` object parameter, which allows us to pass a few different flags, one of which is `once`. Setting `once` to `true` results in the exact same behavior as the snippet above with minimal effort.
|
||||
|
||||
|
||||
@ -65,5 +65,4 @@ for (let i = 0; i < 100; i ++)
|
||||
memoizedFibonacci(30); // ~50ms
|
||||
```
|
||||
|
||||
|
||||
**Image credit:** [Mark Tegethoff](https://unsplash.com/@tegethoff?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/code?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/arrays.jpg
|
||||
excerpt: Arrays are one of the most used data types in any programming language. Learn how to merge two arrays in JavaScript with this short guide.
|
||||
---
|
||||
|
||||
**Spread operator**
|
||||
### Spread operator
|
||||
|
||||
The [spread operator (`...`)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) was introduced in ES6 and can be used to merge two or more arrays, by spreading each one inside a new array:
|
||||
|
||||
@ -18,7 +18,7 @@ const b = [4, 5, 6];
|
||||
const merged = [...a, ...b]; // [1, 2, 3, 4, 5, 6]
|
||||
```
|
||||
|
||||
**Array.prototype.concat()**
|
||||
### Array.prototype.concat()
|
||||
|
||||
[`Array.prototype.concat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat) is a method on the `Array` prototype and can be used to create a new array, either by concatenating both arrays to a new array or one array to the other. Both methods result in a new array, without mutating the original:
|
||||
|
||||
@ -31,7 +31,7 @@ const merged = [].concat(a, b); // [1, 2, 3, 4, 5, 6]
|
||||
const alsoMerged = a.concat(b); // [1, 2, 3, 4, 5, 6]
|
||||
```
|
||||
|
||||
**Comparing the two**
|
||||
### Comparing the two
|
||||
|
||||
The spread operator version is definitely shorter and as readable as the `Array.prototype.concat()` one. Apart from that, the spread operator seems to be slightly faster based on [some benchmarks I have performed](https://jsben.ch/9txyg) (as of **Aug, 2020 on Google Chrome 84** - this might or might not be the case in the future, as new optimizations land in different browsers).
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/javascript-modify-url-without-reload.jpg
|
||||
excerpt: Learn all of the options JavaScript provides for modifying the URL of the current page in the browser without reloading the page.
|
||||
---
|
||||
|
||||
**Using the History API**
|
||||
### Using the History API
|
||||
|
||||
The HTML5 [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) is definitely the way to go for modern websites, as it accomplishes the task at hand, while also providing additional functionality. You can use either `history.pushState()` or `history.replaceState()` to modify the URL in the browser, depending on your needs:
|
||||
|
||||
@ -26,7 +26,7 @@ window.history.replaceState(nextState, nextTitle, nextURL);
|
||||
|
||||
The arguments for both methods are the same, allowing you to pass a customized serializable `state` object as the first argument, a customized `title` (although most browsers will ignore this parameter) and the `URL` you want to add/replace in the browser's history. Bear in mind that the History API only allows same-origin URLs, so you cannot navigate to an entirely different website.
|
||||
|
||||
**Using the Location API**
|
||||
### Using the Location API
|
||||
|
||||
The older [Location API](https://developer.mozilla.org/en-US/docs/Web/API/Location) is not the best tool for the job, as it reloads the page, however it still allows you to modify the current URL and might be useful when working with legacy browsers. You can modify the URL, using either `window.location.href`, `location.assign()` or `location.replace()`:
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/javascript-swap-two-variables.jpg
|
||||
excerpt: Learn everything you need to know about JavaScript modules with this handy cheatsheet.
|
||||
---
|
||||
|
||||
**Named exports**
|
||||
### Named exports
|
||||
|
||||
```js
|
||||
/* environment.js */
|
||||
@ -22,7 +22,7 @@ import { key } from 'environment';
|
||||
- Import and export name should be the same.
|
||||
- Importing requires `{}`.
|
||||
|
||||
**Default exports**
|
||||
### Default exports
|
||||
|
||||
```js
|
||||
/* environment.js */
|
||||
@ -43,7 +43,7 @@ const { key, port } = environment;
|
||||
- Import name can be anything.
|
||||
- Importing does not require `{}`.
|
||||
|
||||
**Default + named**
|
||||
### Default + named
|
||||
|
||||
```js
|
||||
/* environment.js */
|
||||
@ -66,7 +66,7 @@ const { key, port } = environment;
|
||||
- Rules about number of exports and naming conventions apply as before.
|
||||
- Import rules apply as before, can be mixed if necessary.
|
||||
|
||||
**Export list**
|
||||
### Export list
|
||||
|
||||
```js
|
||||
/* environment.js */
|
||||
@ -86,7 +86,7 @@ import { key, port } from 'environment';
|
||||
- Rules about number of exports, naming conventions and import rules are the same as those of named exports.
|
||||
- Export lists are not objects.
|
||||
|
||||
**Rename export**
|
||||
### Rename export
|
||||
|
||||
```js
|
||||
/* environment.js */
|
||||
@ -101,7 +101,7 @@ import { authKey } from 'environment';
|
||||
- Named exports can make use of the `as` keyword to rename an export.
|
||||
- Import name should be the same as the renamed export.
|
||||
|
||||
**Rename import**
|
||||
### Rename import
|
||||
|
||||
```js
|
||||
/* environment.js */
|
||||
@ -114,7 +114,7 @@ import { key as authKey } from 'environment';
|
||||
- Named imports can make use of the `as` keyword to rename an import.
|
||||
- Import name (before the `as` keyword) should be the same as the export.
|
||||
|
||||
**Import all**
|
||||
### Import all
|
||||
|
||||
```js
|
||||
/* environment.js */
|
||||
|
||||
@ -7,14 +7,14 @@ cover: blog_images/js-naming-conventions.jpg
|
||||
excerpt: Naming conventions, while not easy to enforce, make code easier to read and understand. Learn how to name your variables in JavaScript with this handy guide.
|
||||
---
|
||||
|
||||
**Variables**
|
||||
### Variables
|
||||
|
||||
- Names are case-sensitive, lowercase and uppercase are different.
|
||||
- Start variable names with a letter, use `camelCase` for names.
|
||||
- Variable names should be self-descriptive, describing the stored value.
|
||||
- Boolean variables are usually prefixed with `is` or `has`.
|
||||
|
||||
**Functions**
|
||||
### Functions
|
||||
|
||||
- Names are case-sensitive, lowercase and uppercase are different.
|
||||
- Start function names with a letter, use `camelCase` for names.
|
||||
@ -22,20 +22,20 @@ excerpt: Naming conventions, while not easy to enforce, make code easier to read
|
||||
- Common prefixes are `get`, `make`, `apply` etc.
|
||||
- Class methods follow the same rules.
|
||||
|
||||
**Constant**
|
||||
### Constant
|
||||
|
||||
- Names are case-sensitive, lowercase and uppercase are different.
|
||||
- Define constants at the top of your file, function or class.
|
||||
- Sometimes `UPPER_SNAKE_CASE` is used, while other times plain `camelCase`.
|
||||
|
||||
**Classes**
|
||||
### Classes
|
||||
|
||||
- Names are case-sensitive, lowercase and uppercase are different.
|
||||
- Start class names with a capital letter, use `PascalCase` for names.
|
||||
- Use descriptive names, explaining the funcionality of the class.
|
||||
- Components, which are used in frontend frameworks follow the same rules.
|
||||
|
||||
**Private**
|
||||
### Private
|
||||
|
||||
- Prefix any variable or function with `_` to show intention for it to be private.
|
||||
- As a convention, this will not prevent other parts of the code from accessing it.
|
||||
|
||||
@ -9,7 +9,7 @@ excerpt: JavaScript ES2020 introduced optional chaining and nullish coalescing a
|
||||
|
||||
JavaScript ES2020 introduced some new features that help us write cleaner code. Let's take a quick look at two of them that aim to make working with objects and variables a lot easier.
|
||||
|
||||
**Optional chaining**
|
||||
### Optional chaining
|
||||
|
||||
The [optional chaining operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) (`?.`) allows us to access deeply nested object properties without having to validate each reference in the nesting chain. In case of a reference being nullish (`null` or `undefined`) the optional chaining operator will short-circuit, returning `undefined`. The optional chaining operator can also be used with function calls, returning `undefined` if the given function does not exist.
|
||||
|
||||
@ -29,7 +29,7 @@ const userType = data?.user?.type;
|
||||
data.showNotifications?.();
|
||||
```
|
||||
|
||||
**Nullish coalescing**
|
||||
### Nullish coalescing
|
||||
|
||||
In the same spirit, the [nullish coalescing operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator) (`??`) is a logical operator that allows us to check for nullish (`null` or `undefined`) values, returning the right-hand side operand when the value is non-nullish, otherwise returning the left-hand side operand.
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/javascript-range-generator.jpg
|
||||
excerpt: Learn how to use JavaScript ES6 generators and iterators to iterate over ranges of numbers.
|
||||
---
|
||||
|
||||
**Generator functions**
|
||||
### Generator functions
|
||||
|
||||
[JavaScript ES6 generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) allow you to define functions that can be exited and later re-entered, while retaining their context (variable bindings). They are defined using `function*` (`function` keyword followed by an asterisk) and use `yield` expressions to return their result. For example:
|
||||
|
||||
@ -28,7 +28,7 @@ while (!x.done) {
|
||||
|
||||
In the above example, we define a generator function, `generateRange`, which will return each value between `start` and `end`, incrementing by `step` each time. We use the [generator object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) to call `Generator.prototype.next()` until it returns `{value: undefined, done: true}` to iterate over the values the generator produces.
|
||||
|
||||
**Symbol.iterator**
|
||||
### Symbol.iterator
|
||||
|
||||
[`Symbol.iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator) specifies the default iterator for an object. Oftentimes, `Symbol.iterator` is implemented using a generator function. For example:
|
||||
|
||||
@ -45,7 +45,7 @@ console.log([...iterableX]); // [1, 2]
|
||||
|
||||
As you can see in this example, the object is made iterable by assigning a generator function to its `Symbol.iterator` property. This can come especially handy, if you want to iterate over some arbitrary data or create an object that is iterable and uses a generator function under the hood.
|
||||
|
||||
**Putting it all together**
|
||||
### Putting it all together
|
||||
|
||||
Knowing how both concepts work, we can combine them to create a range generator, similar to Python or Ruby's ranges:
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ excerpt: Learn all the different ways you can implement a sleep() function in Ja
|
||||
|
||||
JavaScript does not come with a `sleep()` function out of the box and that is probably a good idea considering the environments where it runs and the trouble such a function could cause if used incorrectly. The closest equivalent of such a function is `setTimeout`, however there are other, less common ways to implement a function that will freeze the current thread for a specified amount of time.
|
||||
|
||||
**setTimeout**
|
||||
### setTimeout
|
||||
|
||||
JavaScript's `setTimeout` sets a timer which executes a function or specified piece of code once the timer expires. Only the code inside the `setTimeout` callback will execute after the timer expires, which can lead to nesting issues, as well as code executing out of order if you are not careful.
|
||||
|
||||
@ -23,7 +23,7 @@ const printNums = () => {
|
||||
printNums(); // Logs: 1, 3, 2 (2 logs after 500ms)
|
||||
```
|
||||
|
||||
**Synchronous version**
|
||||
### Synchronous version
|
||||
|
||||
While strongly discouraged, `Date.prototype.getTime()` can be used inside a `while` loop to pause execution for a set amount of time. You can easily define a synchronous `sleep()` function like this:
|
||||
|
||||
@ -43,7 +43,7 @@ const printNums = () => {
|
||||
printNums(); // Logs: 1, 2, 3 (2 and 3 log after 500ms)
|
||||
```
|
||||
|
||||
**Asynchronous version**
|
||||
### Asynchronous version
|
||||
|
||||
A less intrusive way to go about implementing a `sleep()` function is to utilize the `async` and `await` keywords added in JavaScript ES6, a `Promise` and `setTimeout()`. Note that the resulting function must be executed in an `async` function and has to be called with `await`:
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/u-got-this.jpg
|
||||
excerpt: JavaScript's "this" keyword is a source of confusion for many beginners and veterans alike. Learn how it works in different scenarios and start using it correctly.
|
||||
---
|
||||
|
||||
**What is `this`?**
|
||||
### What is `this`?
|
||||
|
||||
In JavaScript, the `this` keyword refers to the object that is currently executing the code. The short version of what `this` evaluates to is as follows:
|
||||
|
||||
@ -19,7 +19,7 @@ In JavaScript, the `this` keyword refers to the object that is currently executi
|
||||
- In a constructor call, `this` is bound to the new object being constructed.
|
||||
- In an event handler, `this` is bound to the element on which the listener is placed.
|
||||
|
||||
**Global context**
|
||||
### Global context
|
||||
|
||||
In the global execution context, `this` refers to the global object.
|
||||
|
||||
@ -27,7 +27,7 @@ In the global execution context, `this` refers to the global object.
|
||||
console.log(this === window); // true
|
||||
```
|
||||
|
||||
**Function context**
|
||||
### Function context
|
||||
|
||||
When not in strict mode, a function's `this` refers to the global object.
|
||||
|
||||
@ -51,7 +51,7 @@ function f() {
|
||||
console.log(f()); // undefined
|
||||
```
|
||||
|
||||
**Object context**
|
||||
### Object context
|
||||
|
||||
When a function is called as a method of an object, `this` refers to the object the method is called on. This applies to methods defined anywhere in the object's prototype chain (i.e. own and inherited methods).
|
||||
|
||||
@ -81,7 +81,7 @@ const obj = new C();
|
||||
console.log(obj.x); // 10
|
||||
```
|
||||
|
||||
**Arrow function context**
|
||||
### Arrow function context
|
||||
|
||||
In arrow functions, `this` retains the value of the enclosing lexical context's `this`.
|
||||
|
||||
@ -104,7 +104,7 @@ console.log(obj.bar() === window); // true
|
||||
|
||||
Notice how in the second example, an arrow function's `this` refers to the global object unless wrapped inside a regular `function` call, whose `this` refers to the object it's called from and its lexical context is retained by the arrow function.
|
||||
|
||||
**Event handler context**
|
||||
### Event handler context
|
||||
|
||||
When used in an event handler, `this` refers to the element on which the listener is placed.
|
||||
|
||||
@ -116,7 +116,7 @@ el.addEventListener('click', function() {
|
||||
});
|
||||
```
|
||||
|
||||
**Binding `this`**
|
||||
### Binding `this`
|
||||
|
||||
Using `Function.prototype.bind()` returns a new function from an existing one, where `this` is permanently bound to the first argument of `bind()`.
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ Before your JavaScript code is executed, it is first parsed and compiled. During
|
||||
|
||||
Note that only declarations are hoisted, not initializations, meaning that if a variable is declared and initialized after using it, its value will not be initialized. This is an oversimplificatin of the situation, so let's take a look at the different cases:
|
||||
|
||||
**function**
|
||||
### function
|
||||
|
||||
When using `function` declarations, the function can be called before it's defined and it will work as expected. For example:
|
||||
|
||||
@ -27,7 +27,7 @@ hello(); // logs 'Hello world!'
|
||||
|
||||
In the example above the `function` declaration is hoisted to the top of its scope and, due to the nature of function declarations, it's available before it's declared. However, this is the only case that behaves this way.
|
||||
|
||||
**var**
|
||||
### var
|
||||
|
||||
`var` declarations on the other hand behave differently, returning `undefined` when accessed before initialization. For example:
|
||||
|
||||
@ -44,7 +44,7 @@ f(); // returns 'Hi!'
|
||||
|
||||
As you can see in this example, the `var` declarations are hoisted to the top of their scope, but their values are not initialized until the code that initializes them executes, thus being `undefined` up until that point.
|
||||
|
||||
**const and let**
|
||||
### const and let
|
||||
|
||||
Finally, `const` and `let` declarations are hoisted, but they are not initialized to `undefined`. Instead, they will give you an error, which is also how `class` declarations behave. For example:
|
||||
|
||||
@ -61,7 +61,7 @@ f(); // returns 'Hey!'
|
||||
|
||||
Generally, `const` and `let` provide more of a headache-free experience for a variety of reasons and this is no exception. Where accessing variables declared with `var` before initialization fails silently, doing the same for `const` or `let` results in a clear, easy to debug error.
|
||||
|
||||
**Best practices**
|
||||
### Best practices
|
||||
|
||||
- Always define variables, functions, objects and classes before using them. ESLint can probably help you with that.
|
||||
- If your environment/team allows it, prefer `const` and `let`over `var` to minimize headaches.
|
||||
|
||||
@ -9,7 +9,7 @@ excerpt: JavaScript developers often get confused by JavaScript's variables and
|
||||
|
||||
I have seen many developers - my younger self included - struggle with JavaScript's variables and scopes, even if they have some experience with coding and/or the language itself. While there are dozens of great articles on this subject, I found it somewhat difficult to memorize or understand the way these concepts work when I was starting out, so here's a short and simple breakdown that might help you as much as it helped me.
|
||||
|
||||
**Variable definition**
|
||||
### Variable definition
|
||||
|
||||
JavaScript provides two ways to define a variable (`var` and `let`) and one way to define a constant value (`const`).
|
||||
|
||||
@ -19,7 +19,7 @@ It is generally preferred to use `let` and `const` to avoid confusion when it co
|
||||
|
||||

|
||||
|
||||
**Scope**
|
||||
### Scope
|
||||
|
||||
When we talk about scope, we mean the visibility of one or more entities (e.g variables) to certain parts of our code. There are two types of scopes: global and local. Local scope can in turn be separated into block, function and other types of more specific scopes.
|
||||
|
||||
|
||||
@ -7,21 +7,21 @@ cover: blog_images/python-lists-tuples.jpg
|
||||
excerpt: Learn how Python's lists and tuples are different and level up your code today.
|
||||
---
|
||||
|
||||
**Lists**
|
||||
### Lists
|
||||
|
||||
- Syntax: `[1, 2, 3]`
|
||||
- Contained elements are mutable (can be changed after creation)
|
||||
- Lists have a variable length
|
||||
- A list takes up more memory than a tuple
|
||||
|
||||
**Tuples**
|
||||
### Tuples
|
||||
|
||||
- Syntax: `(1, 2, 3)`
|
||||
- Contained elements are immutable (cannot be changed after creation)
|
||||
- Tuples have a fixed length
|
||||
- A tuple takes up less memory than a list
|
||||
|
||||
**When to use each one**
|
||||
### When to use each one
|
||||
|
||||
Lists provide a more accessible API and should be used whenever similar types of objects need to be stored and are expected to change over the course of the application's execution. On the other hand, tuples should be used for immutable data, behaving more like constants than variables.
|
||||
|
||||
|
||||
@ -7,20 +7,20 @@ cover: blog_images/react-rendering.jpg
|
||||
excerpt: Take a deeper dive into React's rendering process and understand the basics behind the popular JavaScript framework.
|
||||
---
|
||||
|
||||
**React rendering**
|
||||
#### React rendering
|
||||
|
||||
- React rendering basics (this blog post)
|
||||
- [React rendering optimization](/blog/s/react-rendering-optimization)
|
||||
- [React rendering state](/blog/s/react-rendering-state)
|
||||
|
||||
|
||||
**Rendering introduction**
|
||||
### Rendering introduction
|
||||
|
||||
**Rendering** is the process during which React moves down the component tree starting at the root, looking for all the components flagged for update, asking them to describe their desired UI structure based on the current combination of `props` and `state`. For each flagged component, React will call its `render()` method (for class components) or `FunctionComponent()` (for function components), and save the output produced after converting the JSX result into a plain JS object, using `React.createElement()`.
|
||||
|
||||
After collecting the render output from the entire component tree, React will diff the new tree (the **virtual DOM**) with the current DOM tree and collect the list of changes that need to be made to the DOM to produce the desired UI structure. After this process, known as **reconciliation**, React applies all the calculated changes to the DOM.
|
||||
|
||||
**Render and commit phases**
|
||||
### Render and commit phases
|
||||
|
||||
Conceptually, this work is divided into two phases:
|
||||
|
||||
@ -34,7 +34,7 @@ Two key takeaways here are the following:
|
||||
- Rendering is not the same as updating the DOM
|
||||
- A component may be rendered without any visible changes
|
||||
|
||||
**Rendering reasons**
|
||||
### Rendering reasons
|
||||
|
||||
After the initial render has completed, there are a few different things that will cause a re-render:
|
||||
|
||||
@ -44,7 +44,7 @@ After the initial render has completed, there are a few different things that wi
|
||||
- `useReducer()` dispatches (function components)
|
||||
- `ReactDOM.render()` again (on the root component)
|
||||
|
||||
**Rendering behavior**
|
||||
### Rendering behavior
|
||||
|
||||
React's default behavior is to **recursively render all child components inside of it when a parent component is rendered**. This means that it does not care if a component's `props` have changed - as long as the parent component rendered, its children will render unconditionally.
|
||||
|
||||
|
||||
@ -7,19 +7,19 @@ cover: blog_images/react-rendering.jpg
|
||||
excerpt: Take a deeper dive into React's rendering process and understand how to make small yet powerful tweaks to optimize performance.
|
||||
---
|
||||
|
||||
**React rendering**
|
||||
#### React rendering
|
||||
|
||||
- [React rendering basics](/blog/s/react-rendering-basics)
|
||||
- React rendering optimization (this blog post)
|
||||
- [React rendering state](/blog/s/react-rendering-state)
|
||||
|
||||
**Optimization opportunities**
|
||||
### Optimization opportunities
|
||||
|
||||
As we've seen in the [previous blog post](/blog/s/react-rendering-basics), **rendering** is React's way of knowing if it needs to make changes in the DOM, but there are certain cases where work and calculations performed during the **render phase** can be a wasted effort. After all, if a component's render output is indentical, there will be no DOM updates, thus the work wasn't necessary.
|
||||
|
||||
Render output should always be based on the current combination of `props` and `state`, so it is possible to know ahead of time if a component's render output will be the same so long as its `props` and `state` remain unchanged. This is the key observation on top of which optimizing React rendering is based, as it hinges on our code doing less work and skipping component rendering when possible.
|
||||
|
||||
**Optimization techniques**
|
||||
### Optimization techniques
|
||||
|
||||
React offers a handful of APIs that allow us to optimize the rendering process:
|
||||
|
||||
@ -29,7 +29,7 @@ React offers a handful of APIs that allow us to optimize the rendering process:
|
||||
|
||||
All of these techniques use **shallow equality** for comparisons. Skipping rendering a component means skipping the default recursive behavior of rendering children, effectively skipping the whole subtree of components.
|
||||
|
||||
**Reference memoization**
|
||||
### Reference memoization
|
||||
|
||||
Passing new references as `props` to a child component doesn't usually matter, as it will re-render regardless when the parent changes. However, if you are trying to optimize a child component's rendering by checking if its `props` have changed, passing new references will cause a render. This behavior is ok if the new references are updated data, but if it's a new reference to the same callback function passed down by the parent, it's rather problematic.
|
||||
|
||||
@ -37,7 +37,7 @@ This is less of an issue in class components, as they have instance methods whos
|
||||
|
||||
`useMemo` and `useCallback` can provide performance benefits but, as with any other memoization usage, it's important to think about their necessity and the net benefit they provide in the long run. A good rule of thumb is to consider using them for pure functional components that re-render often with the same `props` and/or might do heavy calculations and avoid them elsewhere.
|
||||
|
||||
**Performance measurement**
|
||||
### Performance measurement
|
||||
|
||||
**React Developer Tools** provide a handy **Profiler** tab that allows you to visualize and explore the rendering process of your React applications. Under this tab, you will find a settings icon which will allow you to _Highlight updates when components render_, as well as _Record why each component rendered while profiling_ - I highly suggest ticking both of them. Recording the initial render and re-renders of the website can provide invaluable insights about the application's bottlenecks and issues and also highlight optimization opportunities (often using one of the techniques described above).
|
||||
|
||||
|
||||
@ -7,19 +7,19 @@ cover: blog_images/react-rendering.jpg
|
||||
excerpt: Take a deeper dive into React's rendering process and understand the role of the Context API and Redux in it.
|
||||
---
|
||||
|
||||
**React rendering**
|
||||
#### React rendering
|
||||
|
||||
- [React rendering basics](/blog/s/react-rendering-basics)
|
||||
- [React rendering optimization](/blog/s/react-rendering-optimization)
|
||||
- React rendering state (this blog post)
|
||||
|
||||
**Context API**
|
||||
### Context API
|
||||
|
||||
React's **Context API** provides a way to pass data through the component tree without using `props`, but should not be used for state management as it requires manual updating. Any component inside a context's `Provider` can access the data in the context instance using a `Consumer` component or, for function components only, the `useContext` hook.
|
||||
|
||||
When a new reference is passed to a context `Provider` it will cause any connected components to update. React will look for any components consuming the context in the component tree and update them to reflect the change in the context's value. Passing a new object to a context `Provider` is essentially a new reference, as the context holds a single value (in this case an object).
|
||||
|
||||
**Context optimization**
|
||||
### Context optimization
|
||||
|
||||
By default, any update to a parent component that renders a context `Provider` will cause all of the child components to re-render regardless of changes in the context, due to React's rendering process. To avoid re-rendering child components when a parent changes, **memoization** can be used, which will cause React to skip the whole subtree of a skipped component.
|
||||
|
||||
@ -27,13 +27,13 @@ When the context is updated, React additionally checks for components consuming
|
||||
|
||||
Oftentimes, it's a good idea to memoize the component immediately under a context `Provider`. That way updates to the parent component will not cause a re-render for the whole subtree, but only the components that consume the context.
|
||||
|
||||
**React-Redux**
|
||||
### React-Redux
|
||||
|
||||
React-Redux provides bindings for **Redux**, a state container for JavaScript applications, and works a little differently from React's Context API. One of the key differences is that React-Redux only re-renders components that need to render, due to the fact that components subscribed to the Redux store read the latest store state, diff the values and force re-render only if the relevant data has changed, while React is not involved at all in the subscription callback process.
|
||||
|
||||
While this most likely means that fewer components will have to re-render compared to using a context, React-Redux always executes its `mapStateToProps` and `useSelector` functions for every connected component in the tree whenever the store state is updated. These calculations are usually less expensive than React's rendering, but if there are costly calculations performed or new references returned when they shouldn't, it might become problematic.
|
||||
|
||||
**React-Redux optimization**
|
||||
### React-Redux optimization
|
||||
|
||||
React-Redux provides two ways of connecting to its store, performing the necessary work and returning the combined `props`:
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/react-selected-option.jpg
|
||||
excerpt: Learn of all the different ways to set the value of a selected input in React with this quick guide.
|
||||
---
|
||||
|
||||
**Adding selected to an option**
|
||||
### Adding selected to an option
|
||||
|
||||
A very common way of setting a `<select>` input's value is by adding a `selected` attribute to one of its `<option>` elements. For example:
|
||||
|
||||
@ -29,7 +29,7 @@ const Select = ({ values, callback, selected }) => {
|
||||
}
|
||||
```
|
||||
|
||||
**Setting value for the select**
|
||||
### Setting value for the select
|
||||
|
||||
While this approach closely resembles HTML and feels intuitive, there is an easier way to do the same thing. [React](https://reactjs.org/docs/forms.html#the-select-tag) provides us with a shared API between `<input type="text">`, `<textarea>` and `<select>` where we can use `value` or `defaultValue` (depending if the input is controlled or not) to set the field's value.
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/cheatsheet1.jpg
|
||||
excerpt: Regular expressions are a very useful tool in a variety of situations. Save this cheatsheet for any time you need to look up their syntax and speed up your development.
|
||||
---
|
||||
|
||||
**Anchors**
|
||||
### Anchors
|
||||
|
||||
- `^`: start of the string or the start of a line in a multiline pattern
|
||||
- `$`: end of the string or the end of a line in a multiline pattern
|
||||
@ -16,7 +16,7 @@ excerpt: Regular expressions are a very useful tool in a variety of situations.
|
||||
|
||||
Note: Anchors are non-quantifiable (i.e. cannot be followed by a quantifier).
|
||||
|
||||
**Character sequences**
|
||||
### Character sequences
|
||||
|
||||
- `.`: any character except line breaks
|
||||
- `\w`: any word character
|
||||
@ -33,7 +33,7 @@ Note: Anchors are non-quantifiable (i.e. cannot be followed by a quantifier).
|
||||
|
||||
Note: Use `\` to escape special characters (e.g. `\`, `/`, `[`, `]`, `(`, `)`, `{`, `}` etc.).
|
||||
|
||||
**Quantifiers**
|
||||
### Quantifiers
|
||||
|
||||
- `a?`: zero or one of `a` (equal to `a{0,1}`)
|
||||
- `a*`: zero or more of `a` (equal to `a{0,}`)
|
||||
@ -44,13 +44,13 @@ Note: Use `\` to escape special characters (e.g. `\`, `/`, `[`, `]`, `(`, `)`, `
|
||||
|
||||
Note: `a` is any valid quantifiable expression.
|
||||
|
||||
**Groups**
|
||||
### Groups
|
||||
|
||||
- `(ab)`: match and capture everything enclosed (here exactly `ab`)
|
||||
- `(a|b)`: match and capture either one character (here `a` or `b`)
|
||||
- `(?:ab)`: match everything enclosed, without capturing
|
||||
|
||||
**Flags**
|
||||
### Flags
|
||||
|
||||
- `g`: Global
|
||||
- `m`: Multiline
|
||||
|
||||
@ -7,7 +7,7 @@ cover: blog_images/testing-react-side-effects.jpg
|
||||
excerpt: Testing React components that update asynchronously with React Testing Library is a common scenario. Learn how to deal with common issues and speed up your testing.
|
||||
---
|
||||
|
||||
**Components that update asynchronously**
|
||||
### Components that update asynchronously
|
||||
|
||||
Recently, while working on our latest side-project, [boardeaux](https://github.com/Trinityyi/boardeaux), we started using the [React DnD library](https://react-dnd.github.io/react-dnd), as we wanted to implement a multi-container drag and drop system with cards.
|
||||
|
||||
@ -75,7 +75,7 @@ describe('<Card/>', () => {
|
||||
});
|
||||
```
|
||||
|
||||
**The dreaded act(...) warning**
|
||||
### The dreaded `act(...)` warning
|
||||
|
||||
While the test was obviously not working, the console was constantly nagging about wrapping the test in `act()`:
|
||||
|
||||
@ -94,7 +94,7 @@ This message, however, was not very helpful in identifying the underlying issue.
|
||||
|
||||
As a side note, our `Card` component is connected to Redux, which might relate to the issue, but it would most likely happen even without Redux, probably due to the fact that `collect` takes some amount of time to run and send an update to the component.
|
||||
|
||||
**Solving the issue**
|
||||
### Solving the issue
|
||||
|
||||
Digging deeper, we found that apart from `act()`, there are also other options, such as `waitFor()` and `waitForDomChange()`, which seem more intuitive simply because of the name and way they're written (using either `async await` or promises). However, `waitForDomChange()` didn't work properly for our case and our version of `react-testing-library` (which shipped with `react-scripts`) was outdated and did not export `waitFor()`, which took us a good half an hour to figure out.
|
||||
|
||||
@ -151,7 +151,7 @@ describe('<Card/>', () => {
|
||||
});
|
||||
```
|
||||
|
||||
**Summary**
|
||||
### Summary
|
||||
|
||||
- A message about code that causes React state updates not being wrapped in `act(...)` might indicate that a component updated after the test ended.
|
||||
- Using `waitFor()` can solve the issue by making tests asynchronous, but you might need to bump your `react-testing-library` version if you are using older versions of `react-scripts`.
|
||||
|
||||
Reference in New Issue
Block a user