javascript

7 new JavaScript Set methods: union(), intersection() + 5 more

Let’s be honest: you probably don’t care about Sets! At least until now…

They’ve been here since ES6 but they’re usually relegated to making sure a list has no duplicates.

JavaScript
const array = [1, 4, 3, 2, 3, 1]; const noDuplicates = [...new Set(array)]; console.log(noDuplicates); // [1, 4, 3, 2]

With these 7 upcoming built-in Set methods, we could find ourselves using them a LOT more often.

1. union()

The new Set union() method gives us all the unique items in both sets.

JavaScript
const creation = new Set(['coding', 'writing', 'painting']); const joy = new Set(['crying', 'laughing', 'coding']); console.log(creation.union(joy)); // Set { 'coding', 'crying', 'writing', 'laughing', 'painting' }

And since it’s immutable and returns a copy, you can chain it indefinitely:

JavaScript
const odd = new Set([21, 23, 25]); const even = new Set([20, 22, 24]); const prime = new Set([23, 29]); console.log(odd.union(even).union(prime)); // Set(7) { 21, 23, 25, 20, 22, 24, 29 }

2. intersection()

What elements are in both sets?

JavaScript
const mobile = new Set(['javascript', 'java', 'swift', 'dart']); const backend = new Set(['php', 'python', 'javascript', 'java']); const frontend = new Set(['javascript', 'dart']); console.log(mobile.intersection(backend)); // Set { javascript, java } console.log(mobile.intersection(backend).intersection(frontend)); // Set { javascript }

3. difference()

difference() does A – B to return all the elements in A that are not in B:

JavaScript
const joy = new Set(['crying', 'laughing', 'coding']); const pain = new Set(['crying', 'screaming', 'coding']); console.log(joy.difference(pain)); // Set { 'laughing' }

4. symmetricDifference()

As symmetric implies, this method gets the set difference both ways. That’s (A – B) U (B – A).

All the items in 1 and only 1 of the sets:

JavaScript
const joy = new Set(['crying', 'laughing', 'coding']); const pain = new Set(['crying', 'screaming', 'coding']); console.log(joy.symmetricDifference(pain)); // Set { 'laughing', 'screaming' }

5. isSubsetOf()

Purpose is clear: check if all elements of a set are in another set.

JavaScript
const colors = new Set(['indigo', 'teal', 'cyan', 'violet']); const purpleish = new Set(['indigo', 'violet']); const secondary = new Set(['orange', 'green', 'violet']); console.log(purpleish.isSubsetOf(colors)); // true console.log(secondary.isSubsetOf(colors)); // false console.log(colors.isSubsetOf(colors)); // true

6. isSupersetOf()

Check if one set contains all the elements in another set: As good as swapping the two sets in isSubsetOf():

JavaScript
const colors = new Set(['salmon', 'cyan', 'yellow', 'aqua']); const blueish = new Set(['cyan', 'aqua']); const primary = new Set(['red', 'yellow', 'blue']); console.log(colors.isSupersetOf(blueish)); // true console.log(colors.isSupersetOf(primary)); // false console.log(colors.isSupersetOf(colors)); // true

7. isDisjointFrom()

isDisjointFrom: Do these sets share zero common elements?

JavaScript
const ai = new Set(['python', 'c++']); const mobile = new Set(['java', 'js', 'dart', 'kotlin']); const frontend = new Set(['js', 'dart']); console.log(ai.isDisjointFrom(mobile)); // true console.log(mobile.isDisjointFrom(frontend)); // false

Use them now

With core-js polyfills:

Otherwise you get blasted with errors from TypeScript & Node.js — they’re not yet in the official JavaScript standard.

Wrap up

So these are our 7 new Set methods — no more need for 3rd parties like _.intersection() (Lodash!)

JavaScript
const unique = new Set(['salmon', 'cyan', 'cherry', 'aqua']); const blueish = new Set(['cyan', 'aqua', 'blue']); const primary = new Set(['red', 'green', 'blue']); console.log(unique.union(blueish)); // Set { 'salmon', 'cyan', 'cherry', 'aqua', 'blue' } console.log(unique.intersection(blueish)); // Set { 'cyan', 'aqua' } console.log(unique.difference(blueish)); // Set { 'salmon', 'cherry' } console.log(unique.symmetricDifference(blueish)); // Set { 'salmon', 'cherry', 'blue' } console.log(primary.isSubsetOf(unique)); // false console.log(new Set(['red', 'green']).isSubsetOf(primary)); // true console.log(unique.isSupersetOf(new Set(['salmon', 'aqua']))); // true console.log(unique.isSupersetOf(blueish)); // false console.log(unique.isDisjointFrom(primary)); // true console.log(unique.isDisjointFrom(blueish)); // false

Loop through HTML child elements/nodes in JavaScript

In JavaScript, working with the Document Object Model (DOM) often involves iterating through child elements of a parent element. This technique is essential for tasks such as:

  • Manipulating elements based on their content or attributes
  • Dynamically adding or removing elements
  • Handling events for multiple elements

JavaScript offers several methods to achieve this, each with its own advantages and considerations.

Methods for looping

1. Use children property

  • Access the children property of the parent element to obtain a live NodeList of its direct child elements.
  • Iterate through the NodeList using a for loop or other methods:
JavaScript
const parent = document.getElementById("myParent"); const children = parent.children; for (let i = 0; i < children.length; i++) { const child = children[i]; // Perform actions on the child element console.log(child.textContent); }

2. Use for..of loop

Directly iterate over the NodeList using the for...of loop:

JavaScript
const parent = document.getElementById("myParent"); for (const child of parent.children) { // Perform actions on the child element console.log(child.tagName); }

3. Use Array.from() method

Convert the NodeList into an array using Array.from(), allowing the use of array methods like forEach():

JavaScript
const parent = document.getElementById("myParent"); const childrenArray = Array.from(parent.children); childrenArray.forEach(child => { // Perform actions on the child element child.style.color = "red"; });

Key considerations

  • Live NodeList: The children property returns a live NodeList, meaning changes to the DOM are reflected in the NodeList.
  • Text Nodes: The children property includes text nodes, while childNodes includes all types of nodes (elements, text, comments, etc.). Choose the appropriate property based on your needs.
  • Performance: For large DOM trees, using Array.from() might have a slight performance overhead due to array creation.

Choosing the right method

  • For simple iterations, the for...of loop or the children property with a for loop are often sufficient.
  • If you need to use array methods or want to create a static copy of the child elements, use Array.from().
  • Consider performance implications if dealing with large DOM structures.

By understanding these methods and their nuances, you’ll be able to effectively loop through child elements in JavaScript for various DOM manipulation tasks.

Top 10 new JavaScript features from 2021 to 2023

These 10 amazing features were all about writing shorter, safer, and more expressive code.

1. Private methods and fields

We need our privacy. There’s no two ways about it.

And so do OOP classes; but in JavaScript it was once impossible to strictly declare private members.

It was once impossible to declare private members in a JavaScript class.

A member was traditionally prefixed with an underscore (_) to indicate that it was meant to be private, but it could still be accessed and modified from outside the class.

JavaScript
class Person { _firstName = 'Coding'; _lastName = 'Beauty'; get name() { return `${this._firstName} ${this._lastName}`; } } const person = new Person(); console.log(person.name); // Coding Beauty // Members intended to be private can still be accessed // from outside the class console.log(person._firstName); // Coding console.log(person._lastName); // Beauty // They can also be modified person._firstName = 'Debugging'; person._lastName = 'Nightmares'; console.log(person.name); // Debugging Nightmares

With ES2022, we can now add private fields and members to a class, by prefixing it with a hashtag (#). Trying to access them from outside the class will cause an error:

JavaScript
class Person { #firstName = 'Coding'; #lastName = 'Beauty'; get name() { return `${this.#firstName} ${this.#lastName}`; } } const person = new Person(); console.log(person.name); // SyntaxError: Private field '#firstName' must be // declared in an enclosing class console.log(person.#firstName); console.log(person.#lastName);

Note that the error thrown here is a syntax error, which happens at compile time; the code doesn’t run at all; the compiler doesn’t expect you to even try to access private fields from outside a class, so it assumes you’re trying to declare one.

“Ergonomic brand” checks for private fields

With private fields come a new keyword to safely check if a class object contains a particular one — the in keyword:

JavaScript
class Car { #color; hasColor() { return #color in this; } } const car = new Car(); console.log(car.hasColor()); // true;

It correctly distinguishes private fields with the same names from different classes:

JavaScript
class Car { #color; hasColor() { return #color in this; } } class House { #color; hasColor() { return #color in this; } } const car = new Car(); const house = new House(); console.log(car.hasColor()); // true; console.log(car.hasColor.call(house)); // false console.log(house.hasColor()); // true console.log(house.hasColor.call(car)); // false

And don’t ask me about the name; I also have no idea why they’re called that (do you?).

Ergonomics as far as I’m concerned is all about keeping good sitting posture while using your computer (?) 🤔.

Although I guess you could twist this definition to allow for this new feature — or any new feature for that matter. They’re all about comfort, right? Less pain writing code.

But I guess English isn’t a closed language and you can always add new words and additional definitions (just ask Shakespeare).

2. Immutable sort(), splice(), and reverse()

ES2023 came fully packed with immutable versions of these 3 heavily used array methods.

Okay maybe splice() isn’t used as much as the others, but they all mutate the array in place.

JavaScript
const original = [5, 1, 3, 4, 2]; const reversed = original.reverse(); console.log(reversed); // [2, 4, 3, 1, 5] (same array) console.log(original); // [2, 4, 3, 1, 5] (mutated) const sorted = original.sort(); console.log(sorted); // [1, 2, 3, 4, 5] (same array) console.log(original); // [1, 2, 3, 4, 5] (mutated) const deleted = original.splice(1, 2, 7, 10); console.log(deleted); // [2, 3] (deleted elements) console.log(original); // [1, 7, 10, 4, 5] (mutated)

Immutability gives us predictable and safer code; debugging is much easier as we’re certain variables never change their value.

Arguments are exactly the same, with splice() and toSpliced() having to differ in their return value.

JavaScript
const original = [5, 1, 3, 4, 2]; const reversed = original.toReversed(); console.log(reversed); // [2, 4, 3, 1, 5] (copy) console.log(original); // [5, 1, 3, 4, 2] (unchanged) const sorted = original.toSorted(); console.log(sorted); // [1, 2, 3, 4, 5] (copy) console.log(original); // [5, 1, 3, 4, 2] (unchanged) const spliced = original.toSpliced(1, 2, 7, 10); console.log(spliced); // [1, 7, 10, 4, 5] (copy) console.log(original); // [5, 1, 3, 4, 2] (unchanged)

3. Top-level await

Did you know: F# was the first language to introduce async/await? As far back as 2007! But it took JavaScript 10 good years to catch up.

await pauses execution in the async context until a Promise resolves.

Previously we could only use this operator in an async function, but it could never work in the global scope.

JavaScript
function setTimeoutAsync(timeout) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, timeout); }); } // SyntaxError: await is only valid in async functions await setTimeoutAsync(3000);

With ES2022, now we can:

JavaScript
function setTimeoutAsync(timeout) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, timeout); }); } // ✅ Waits for timeout - no error thrown await setTimeoutAsync(3000);

4. Promise.any()

If you know Promise.all(), then you can easily guess what this does: wait for one Promise to resolve and return the result.

JavaScript
async function getHelpQuickly() { const response = await Promise.any([ cautiousHelper(), kindHelper(), wickedHelper(), ]); console.log(response); // Of course! } async function cautiousHelper() { await new Promise((resolve) => { setTimeout(() => { resolve('Uum, oohkaay?'); }, 2000); }); } async function kindHelper() { return 'Of course!'; } function wickedHelper() { return Promise.reject('Never, ha ha ha!!!'); }

Point to note: Promise.any() still waits for *all* the promises in the current async context to resolve, even though it only returns the result of the first one.

JavaScript
await getHelpQuickly(); // outputs "Of course!" immediately // Still waits for 2 seconds

5. Array find from last

Array find() searches for an array element that passes a specified test condition, and findIndex() gets the index of such an element.

While find() and findIndex() both start searching from the first element of the array, there are instances where it would be preferable to start the search from the last element instead.

There are scenarios where we know that finding from the last element might achieve better performance. For example, here we’re trying to get the item in the array with the value prop equal to y. With find() and findIndex():

JavaScript
const letters = [ { value: 'v' }, { value: 'w' }, { value: 'x' }, { value: 'y' }, { value: 'z' }, ]; const found = letters.find((item) => item.value === 'y'); const foundIndex = letters.findIndex((item) => item.value === 'y'); console.log(found); // { value: 'y' } console.log(foundIndex); // 3

This works, but as the target object is closer to the tail of the array, we might be able to make this program run faster if we use the new ES2022 findLast() and findLastIndex() methods to search the array from the end.

JavaScript
const letters = [ { value: 'v' }, { value: 'w' }, { value: 'x' }, { value: 'y' }, { value: 'z' }, ]; const found = letters.findLast((item) => item.value === 'y'); const foundIndex = letters.findLastIndex((item) => item.value === 'y'); console.log(found); // { value: 'y' } console.log(foundIndex); // 3

Another use case might require that we specifically search the array from the end to get the correct item.

If we’re finding the last even number in a list of numbers, find() and findIndex() produces a totally wrong result:

JavaScript
const nums = [7, 14, 3, 8, 10, 9]; // gives 14, instead of 10 const lastEven = nums.find((value) => value % 2 === 0); // gives 1, instead of 4 const lastEvenIndex = nums.findIndex((value) => value % 2 === 0); console.log(lastEven); // 14 console.log(lastEvenIndex); // 1

We could call the reverse() method on the array to reverse the order of the elements before calling find() and findIndex(). But this approach would cause unnecessary mutation of the array; reverse() reverses the elements of an array in place.

The only way to avoid this mutation would be to make a new copy of the entire array, which could cause performance problems for large arrays.

Also findIndex() still wouldn’t on the reversed array, as reversing the elements would also mean changing the indexes they had in the original array. To get the original index, we would need to perform an additional calculation, which means writing more code.

JavaScript
const nums = [7, 14, 3, 8, 10, 9]; // Copying the entire array with the spread syntax before // calling reverse() const reversed = [...nums].reverse(); // correctly gives 10 const lastEven = reversed.find((value) => value % 2 === 0); // gives 1, instead of 4 const reversedIndex = reversed.findIndex((value) => value % 2 === 0); // Need to re-calculate to get original index const lastEvenIndex = reversed.length - 1 - reversedIndex; console.log(lastEven); // 10 console.log(reversedIndex); // 1 console.log(lastEvenIndex); // 4

It’s cases like where the findLast() and findLastIndex() methods come in handy.

JavaScript
const nums = [7, 14, 3, 8, 10, 9]; const lastEven = nums.findLast((num) => num % 2 === 0); const lastEvenIndex = nums.findLastIndex((num) => num % 2 === 0); console.log(lastEven); // 10 console.log(lastEvenIndex); // 4

This code is shorter and more readable. Most importantly, it produces the correct result.

6. String replaceAll()

We already had replace() for quickly replace a substring within a string.

JavaScript
const str = 'JavaScript is so terrible, it is unbelievably terrible!!'; const result = str.replace('terrible', 'wonderful'); console.log(result); // JavaScript is so wonderful, it is unbelievably terrible!! // Huh?

But it only does so for the first occurrence of the substring unless you use a regex; now we have replaceAll() to replace every single instance of that substring.

JavaScript
const str = 'JavaScript is so terrible, it is unbelievably terrible.'; const result = str.replaceAll('terrible', 'wonderful'); console.log(result); // JavaScript is wonderful, it is unbelievably wonderful. // Now you're making sense!

7. Array with() and at()

at() came first and with() came a year after that in 2023.

They are the functional and immutable versions of single-element array modification and access.

JavaScript
const colors = ['pink', 'purple', 'red', 'yellow']; console.log(colors.at(1)); // purple console.log(colors.with(1, 'blue')); // ['pink', 'blue', 'red', 'yellow'] // Original not modified console.log(colors); // ['pink', 'purple', 'red', 'yellow']

The cool thing about these new methods is how they let you get and change element values with negative indexing.

JavaScript
// index -N is same as index arr.length - N const fruits = ['banana', 'apple', 'orange', 'butter???']; console.log(fruits.at(-3)); // apple console.log(fruits.at(-1)); // butter??? console.log(fruits.with(-1, 'watermelon')); // ['banana', 'apple', 'orange', 'watermelon'] ✅

8. static static static

Static class fields, static private methods (2022).

Static methods access other private/public static members in the class using the this keyword; instance methods with this.constructor:

JavaScript
class Person { static #count = 0; static getCount() { return this.#count; } // Instance method constructor() { this.constructor.#incrementCount(); } static #incrementCount() { this.#count++; } } const person1 = new Person(); const person2 = new Person(); console.log(Person.getCount()); // 2

Static blocks.

Executed only once when the *class* is created. It’s like static constructors in other OOP languages like C# and Java.

JavaScript
class Vehicle { static defaultColor = 'blue'; } class Car extends Vehicle { static colors = []; static { this.colors.push(super.defaultColor, 'red'); } static { this.colors.push('green'); } } console.log(Car.colors); // [ 'blue', 'red', 'green' ]

When they’re multiple static blocks, they’re executed in the order they’re declared, along with any static fields in between. The super property in a static block to access properties of the superclass.

9. Logical assignment operators

They let a variable perform a logical operation with another variable and re-assign the result to itself.

We use them like this:

JavaScript
left ??= right; left ||= right; left &&= right;

They’re as good as:

JavaScript
// More like exactly the same as left = (left ?? right); left = (left || right); left = (left && right);

??=. Quickly assign a value to a variable *if* it is null or undefined (“nullish”).

JavaScript
user.preferredName ??= generateDumbUserName();

||=. Like ??=, but assigns the value for any falsy value (0, undefined, null, '', NaN, or false).

JavaScript
user.profilePicture ||= "/angry-stranger.png";

And then &&=. Something like the reverse; only assigns when the value is truthy (not falsy).

JavaScript
user.favoriteLanguage = await setFavoriteLanguage(input.value); user.favoriteLanguage &&= 'Assembly'; // You're lying! It's Assembly!

10. Numerical separators

Tiny new addition to make big number literals more readable and human-friendly.

JavaScript
const isItPi = 3.1_415_926_535; const isItAvagadro = 602_214_076_000_000_000_000_000; const isItPlanck = 6.626_070_15e-34; const isItG = 6.674_30e-11; // Works for other number bases too...

The compiler completely ignores those pesky underscores — they’re all for you, the human!

Final thoughts

These are the juicy new JavaScript features that arrived in the last 3 years. Use them to boost your productivity as a developer and write cleaner code with greater conciseness, expressiveness and clarity.

Master JavaScript generators: 5 inspiring practical use cases

You will be an expert on JavaScript generators by the time you’re done reading this.

JavaScript generators are way more than just a fancy feature and we are going to discover many powerful use cases for them, including creating engaging animations, and streaming videos over the internet.

If you’ve never heard of them you may be missing out.

Generators are simply functions that you can pause and resume whenever you want — they don’t execute continuously.

The asterisk * marks the function as a generator, and yield generates values on demand from a .next() call, until the generator is done.

JavaScript
function* myGenerator() { yield 'Hello'; yield 'World'; } const iterator = myGenerator(); console.log(iterator.next()); // {value: 'Hello', done: false} console.log(iterator.next()); // {value: 'World', done: false} console.log(iterator.next()); // {value: undefined, done: true}

It’s just like how a physical generator produces electricity not all at once, but as time goes on.

Instead of calling next() you can use the for..of loop, which is great for when the generator generates a lot of data.

JavaScript
function* countTo100() { for (let i = 0; i < 100; i++) { yield i; } } for (const item of countTo100()) { console.log(item); } // 1, 2, ... 100

Lazy evaluation

“Calculate only when necessary.”

Much unlike regular functions in JavaScript that execute entirely and return the result.

Let’s say you want a sequence of numbers, but you’re not sure how many. Here’s how a generator helps:

JavaScript
function* numberGenerator() { let number = 1; // infinite loop won't cause freeze in generator // -- execution pauses after `yield` while (true) { yield number++; } } const numbers = numberGenerator(); console.log(numbers.next().value); // 1 console.log(numbers.next().value); // 2 // you can continue this as long as you need

With generators, you get the next number only when you ask for it.

Better memory utilization

Generators don’t hold all the results in memory, they generate them on the fly.

Imagine you need a sequence of a million numbers. With a regular function, you’d need to store all these numbers in an array, using up significant memory.

A generator is much more efficient:

JavaScript
function* bigNumberGenerator() { let number = 1; while (number <= 100000000) { yield number++; } } const bigNumbers = bigNumberGenerator(); const chunk = 10; for (let i = 0; i < chunk; i++) { const value = bigNumbers.next().value; // Use next 10 values... }

Handling asynchronous tasks

Did you know that Babel transpiles async/await to generators for JavaScript versions that don’t support it natively?

Babel tranforms this:

JavaScript
this.it('is a test', async function () { const name = await 'coding beauty' const num = await new Promise(resolve => resolve(10)); console.log(`Name: ${name}, Num: ${num}`); });

To this:

JavaScript
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then( function (value) { return step('next', value); }, function (err) { return step('throw', err); } ); } } return step('next'); }); }; } myFunc( 'generator async/await example', _asyncToGenerator(function* () { const name = yield 'coding beauty'; // yield, not await const num = yield new Promise((resolve) => resolve(10)); console.log(`Name: ${name}, Num: ${num}`); }) );

Typing animations

Typing animations grab the attention of users and make your website more visually appealing.

They add personality and character to a website by mimicking the typing behavior of a human to create a more human-like experience and establish a unique brand identity.

So with all these benefits you’re feeling excited about infusing your webpages with these energetic visual effects.

Here would be a decent way to go about it, using recursion and setTimeout():

JavaScript
function typeAnimation(text, index = 0) { if (index < text.length) { document.body.innerHTML += text.charAt(index); index++; setTimeout(typeAnimation, 500); } }

But, it’s cases like this where generators shine:

JavaScript
function* typeAnimation(text) { for(let char of text) { yield char; } }

Since we can generate values whenever we want, we can do this in time intervals using setInterval().

JavaScript
const generatorInstance = typeGenerator("Coding Beauty"); function startTypingEffect(elementId, generatorInstance) { let element = document.getElementById(elementId); setInterval(() => { let result = generatorInstance.next(); !result.done && element.innerHTML += result.value; }, 100); } startTypingEffect("type-here", generatorInstance);
JavaScript
<div id="type-here"></div>

Asynchronous handling

Note: This is NOT the same as being the bedrock of async/await, like we saw earlier. We’re talking about async generators here.

JavaScript
function* asyncGenerator() { yield new Promise(resolve => setTimeout(() => resolve('Task 1 completed'), 2000)); yield new Promise(resolve => setTimeout(() => resolve('Task 2 completed'), 3000)); // ... }

And here’s how we can use this generator:

JavaScript
const asyncGen = asyncGenerator(); asyncGen.next().value.then(console.log); asyncGen.next().value.then(console.log);

This is such a powerful tool for streaming data in web app in a structured, readable manner — just look at this function that buffers and streams data for a video-sharing app like YouTube:

JavaScript
async function* streamVideo({ id }) { let endOfVideo = false; const downloadChunk = async (sizeInBytes) => { const response = await fetch( `api.example.com/videos/${id}` ); const { chunk, done } = await response.json(); if (done) endOfVideo = true; return chunk; }; while (!endOfVideo) { const bufferSize = 500 * 1024 * 1024; yield await downloadChunk(bufferSize); } }

To consume this async generator, we’d use the for await..of loop:

JavaScript
for await (const chunk of streamVideo({ id: 2341 })) { // process video chunk }

redux-saga

redux-saga is a library for managing side effects in applications, boasting over 1 million weekly downloads.

Generators play a big role in this library, handling the redux actions to make testing and error handling easier.

Take a look at this simple saga:

JavaScript
import { call, put, takeEvery } from 'redux-saga/effects'; function* fetchData(action) { try { const data = yield call(Api.fetchUser, action.payload.userId); yield put({type: "USER_FETCH_SUCCEEDED", data}); } catch (e) { yield put({type: "USER_FETCH_FAILED", message: e.message}); } } function* mySaga() { yield takeEvery("USER_FETCH_REQUESTED", fetchData); }
JavaScript
import createSagaMiddleware from 'redux-saga'; import { createStore, applyMiddleware } from 'redux'; import mySaga from './sagas'; const sagaMiddleware = createSagaMiddleware(); const store = createStore( reducer, applyMiddleware(sagaMiddleware) ); sagaMiddleware.run(mySaga)

Whenever the USER_FETCH_REQUESTED action is dispatched, redux-saga runs the generator which in turn calls fetchData() to perform the asynchronous network request.

A note on return

What happens when you return a value in a generator function? Let’s see:

JavaScript
function* soccerPlayers() { yield 'Ronaldo'; yield 'Messi'; return 'Neymar'; } for (const player of soccerPlayers()) { console.log(player); } // Ronaldo // Messi

Why isn’t Neymar part of the generated values?

Let’s use .next() to find out if the done property has something to do with it:

JavaScript
function* soccerPlayers() { yield 'Ronaldo'; yield 'Messi'; return 'Neymar'; } const playerGen = soccerPlayers(); console.log(playerGen.next()); console.log(playerGen.next()); console.log(playerGen.next()); /* { value: 'Ronaldo', done: false } { value: 'Messi', done: false } { value: 'Neymar', done: true } */

It turns out that return is not considered a generated value, so for..of doesn’t process it.

Do you remember our very first example:

JavaScript
function* myGenerator() { yield 'Hello'; yield 'World'; } const iterator = myGenerator(); console.log(iterator.next()); // {value: 'Hello', done: false} console.log(iterator.next()); // {value: 'World', done: false} console.log(iterator.next()); // {value: undefined, done: true}

You can see that generators only produce values until, but not including when done is true.

So return completes the generator and terminates the function (like any other).

JavaScript
function* myGenerator() { yield 'Hello'; return 'End'; yield 'This will not be executed'; }

Final thoughts

JavaScript generators offer powerful solutions for control flow, memory efficiency, and asynchronous handling. They enhance web development with dynamic animations, streaming data, and managing side effects.

Let’s embrace the versatility of generators for elegant and efficient JavaScript programming.

[SOLVED] Cannot use import statement outside a module in JavaScript

Are you currently experiencing the “Cannot use import statement outside a module” error in JavaScript?

The "Cannot use import statement outside a module" error in a browser console.
The “Cannot use import statement outside a module” error in a browser console.

This is a well-known error that happens when you use the import keyword to load a file or package that is not a module.

Let’s learn how to easily fix this error in Node.js or the browser.

Fix “Cannot use import statement outside a module” in browser

Fix: Set type="module" in <script> tag

The “Cannot use import statement outside a module” error happens in a browser when you import a file that is a module without indicating this in the <script> tag.

index.html
<!DOCTYPE html> <html> <head> <title>Coding Beauty</title> <link rel="icon" href="favicon.ico" /> </head> <body> A site to make you enjoy coding <!-- ❌ SyntaxError: Cannot use import statement outside a module --> <script src="index.js"></script> </body> </html>

To fix it, add type="module" to the <script> tag.

index.html
<!DOCTYPE html> <html> <head> <title>Coding Beauty</title> <link rel="icon" href="favicon.ico" /> </head> <body> A site to make you enjoy coding <!-- ✅ Loads script successfully --> <script type="module" src="index.js"></script> </body> </html>

Fix “Cannot use import statement outside a module” in Node.js

The “Cannot use import statement outside a module” error happens when you use the import keyword in a Node.js project that doesn’t use the ES module import format.

index.js
// ❌ SyntaxError: Cannot use import statement outside a module import axios from 'axios'; const { data } = ( await axios.post('https://api.example.com/hi', { hello: 'world', }) ).data; console.log(data);

Fix: Set type: "module" in package.json

To fix the error in Node.js, set the package.json type field to “module”.

package.json
{ // ... "type": "module", // ... }

Now you can use the import statement with no errors:

index.js
// ✅ Imports successfully import axios from 'axios'; const { data } = ( await axios.post('https://api.example.com/hi', { hello: 'world', }) ).data; console.log(data);

If there’s no package.json in your project, you can initialize one with the npm init -y command.

Shell
npm init -y

The -y flags creates the package.json with all the default options – no user prompts for the fields.

Fix: Use require instead of import

Alternatively, you can fix the “Cannot use import statement outside a module” error in JavaScript by using the require() function in place of the import syntax:

index.js
// ✅ Works - require instead of import const axios = require('axios'); const { data } = ( await axios.post('https://api.example.com/hi', { hello: 'world', }) ).data; console.log(data);

But require() only works on the older, Node-specific CommonJS module system, and these days more and more modules are dropping support for CommonJS in favor of ES6+ modules.

For example, if you use require() for modules like got, node-fetch, and chalk, you’ll get the “require of ES modules is not supported error”.

index.js
// ❌ [ERR_REQUIRE_ESM]: require() of ES Module not supported const chalk = require('chalk'; console.log(chalk.blue('Coding Beauty'));

If you can’t switch to an ES module environment, you can fix this by dynamically importing the module with the async import() function. For example:

index.js
// CommonJS module // ✅ Imports successfully const chalk = (await import('chalk')).default; console.log(chalk.blue('Coding Beauty'));

Or, you can downgrade to an earlier version of the package that supported CommonJS and require().

Shell
npm install chalk@4 # Yarn yarn add got@4

Here are the versions you should install for various well-known NPM packages to avoid this ERR_REQUIRE_ESM error:

Package versions for downgrading to fix the "Cannot use import statement outside a module" error in JavaScript.

Fix “Cannot use import statement outside a module” in TypeScript

If the module field in tsconfig.json is set to commonjs, any import statement in the TypeScript source will change to require() calls after the compilation and potentially cause this error.

tsconfig.json
{ //... "type": "module" }

Prevent this by setting module to esnext or nodenext

tsconfig.json
{ "type": "module" }

nodenext indicates to the TypeScript compiler that the project can emit files in either ESM or CommonJS format.

[Solved] Cannot find module in Node.js (MODULE_NOT_FOUND)

Are you experiencing the “Cannot find module” or MODULE_NOT_FOUND error in your Node.js project?

This error happens when your IDE can’t detect the presence of a particular NPM package. Let’s see how easy it is to fix.

In this article

1. Ensure NPM package installed

To fix the “Cannot find module” error in Node.js, make sure the NPM package is installed and present in your package.json file.

You can install a package from NPM with the npm i command, for example:

Shell
# NPM npm i nextjs-current-url # Yarn yarn add nextjs-current-url # PNPM pnpm add nextjs-current-url

After installation, the package will included under the dependencies field in package.json

package.json
{ ... "dependencies": { ... "nextjs-current-url": "^1.0.1" ... } ... }
Installed NPM packages are in the package.json dependencies key

You can also install the package as a development dependency, which indicates that the package is only used for development, and won’t be needed by the app itself. Packages like nodemon and ts-node fit this category:

Shell
# NPM npm i -D ts-node # Yarn yarn add -D ts-node # PNPM pnpm add -D ts-node

It will be part of devDependencies in package.json after installation:

package.json
{ ... "devDependencies": { ... "ts-node": "^10.9.1" ... } }

2. Install package again

To fix the MODULE_NOT_FOUND error in Node.js, trying installing the package in your project once again, even if you did so earlier:

Shell
# NPM npm i -D try-catch-fn # Yarn yarn add try-catch-fn # NPM yarn add try-catch-fn

3. Reinstall all packages

Try removing the NPM packages installed in your project and reinstalling them again, to fix the “Cannot find module” error.

You can do this with the following command sequence:

Shell
# NPM rm package-lock.json rm node_modules -r npm cache clear npm install # Yarn rm yarn.lock rm node_modules -r yarn cache clean yarn install # PNPM rm pnpm-lock.yaml rm node_modules -r pnpm store prune pnpm install

npm cache clear helps to rid the package manager cache of any corrupted module files.

4. TypeScript: Install type definitions

The MODULE_NOT_FOUND error will occur when you import a package that doesn’t have any detected type definitions into a TypeScript file.

If it’s a core Node.js module like http or fs, you may need to add "node" to compilerOptions.types in your tsconfig.json file:

tsconfig.json
{ // ... "compilerOptions": { // ... "types": [ "node" ] // ... }, // ... }

If it’s a third-party module, installing the type definitions from NPM should help. For example:

JavaScript
npm i @types/express

5. Ensure package.json main file exists

You may encounter the “Cannot find module” error in Node.js if the main field of your package.json file doesn’t exist.

The file in the package.json main field doesn't exists.

You can also try the npm link command on the package to fix the MODULE_NOT_FOUND error, for example:

Shell
npm link create-react-app npm link webpack

npm link is a command that connects a globally installed package with a local project using a symbolic link.

It enables working on a package locally without publishing it to the npm registry or reinstalling it for every change. Executing npm link in the package directory establishes a symbolic link in the global node_modules directory, directing to the local package.

Afterwards, npm link <package-name> can be used in the project to link the global package with your local project.

7. Ensure correct NODE_PATH

In older Node.js versions, you may be able to fix the “Cannot find module” error by setting the NODE_PATH environment variable to correct node_modules installation folder.

NODE_PATH is a string of absolute paths separated by colons used by Node.js to locate modules when they can’t be found elsewhere.

It was initially created to enable the loading of modules from different paths when there was no defined module resolution algorithm.

And it’s still supported, but it’s not as important anymore since the we’ve established a convention for finding dependent modules in Node.js community.

How to get the difference between two arrays in JavaScript

Get asymmetric difference between two arrays

To get the difference between two arrays in JavaScript, use the filter() and include() array methods, like this:

JavaScript
function getDifference(arrA, arrB) { return arrA.filter((element) => !arrB.includes(element)); } const arr1 = [1, 2, 3, 4]; const arr2 = [2, 4]; console.log(getDifference(arr1, arr2)); // [1, 3]

Array filter() runs a callback on every element of an array and returns an array of elements that the callback returns true for:

JavaScript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const evenNumbers = numbers.filter( (number) => number % 2 === 0 ); console.log(evenNumbers); // [2, 4, 6, 8, 10]

The Array includes() method returns true if the Array contains a particular element and returns false if it doesn’t:

JavaScript
const arr = ['a', 'b', 'c']; console.log(arr.includes('b')); // true

If we wanted to find the difference between two Sets, we’d have used Set has() in place of Array includes():

JavaScript
function getDifference(setA, setB) { return new Set( [...setA].filter(element => !setB.has(element)) ); } const set1 = new Set([1, 2, 3, 4]); const set2 = new Set([2, 4]); console.log(getDifference(set1, set2)); // {1, 3}

Get symmetric difference between two arrays

The method above only gives the methods in the second array that aren’t in the first:

JavaScript
function getDifference(arrA, arrB) { return arrA.filter((element) => !arrB.includes(element)); } const arr1 = [1, 3]; const arr2 = [1, 3, 5, 7]; console.log(getDifference(arr1, arr2)); // []

You want this sometimes, especially if arr2 is meant to be arr1‘s subset.

But other times you may want to find the symmetric difference between the arrays; regardless of which one comes first.

To do that, we simply merge the results of two getDifference() calls, each with the order of the arrays reversed:

JavaScript
function getDifference(arrA, arrB) { return arrA.filter((element) => !arrB.includes(element)); } function getSymmetricDifference(arrA, arrB) { return [ ...getDifference(arrA, arrB), ...getDifference(arrB, arrA), ]; } const arr1 = [1, 3]; const arr2 = [1, 3, 5, 7]; console.log(getSymmetricDifference(arr1, arr2)); // [5, 7] console.log(getSymmetricDifference(arr2, arr1)); // [5, 7]

How to get the difference between two sets in JavaScript

Get asymmetric difference between two sets

To get the difference between two sets in JavaScript, use the Array filter() and Set has() methods like this:

JavaScript
function getDifference(setA, setB) { return new Set( [...setA].filter(element => !setB.has(element)) ); } const set1 = new Set([1, 2, 3, 4]); const set2 = new Set([2, 4]); console.log(getDifference(set1, set2)); // {1, 3}

The Set has() method returns true if the Set contains a particular element and returns false if it doesn’t.

JavaScript
const arr = ['a', 'b', 'c']; const set = new Set(arr); console.log(set.has('a')); // true

Array filter() runs a callback on every element of an array and returns an array of elements that the callback returns true for.

JavaScript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const evenNumbers = numbers.filter( (number) => number % 2 === 0 ); console.log(evenNumbers); // [2, 4, 6, 8, 10]

The spread syntax (...) converts the set to an array for filter() to work.

The Set() constructor converts the result of filter() back to an Set.

Get symmetric difference between two sets

The method above only gives the elements in the second set that aren’t in the first.

JavaScript
function getDifference(setA, setB) { return new Set( [...setA].filter((element) => !setB.has(element)) ); } const set1 = new Set([2, 4]); const set2 = new Set([1, 2, 3, 4]); // Every item in set1 is also in set2, but the sets are different console.log(getDifference(set1, set2)); // {}

Sometimes you want this, especially if set2 is supposed to be a set1‘s subset.

But other times you may want to find the symmetric difference between the sets, regardless of which one comes first.

To do that, we simply merge the results of two getDifference() calls, each with the order of the Sets reversed.

JavaScript
function getDifference(setA, setB) { return new Set( [...setA].filter((element) => !setB.has(element)) ); } function getSymmetricDifference(setA, setB) { return new Set([ ...getDifference(setA, setB), ...getDifference(setB, setA), ]); } const set1 = new Set([2, 4]); const set2 = new Set([1, 2, 3, 4]); console.log(getSymmetricDifference(set1, set2)); // {1, 3} console.log(getSymmetricDifference(set2, set1)); // {1, 3}

How to easily detect when the mouse leaves the browser window in JavaScript

Detecting when the mouse leaves the browser window helps to track user engagement and display custom messages or popups.

Let’s learn how to do it with JavaScript.

Detect browser window mouse exit

To detect when the mouse leaves the browser window, use the mouseleave event listener:

index.html
<!DOCTYPE html> <html> <head> <title>Coding Beauty Tutorial</title> <link rel="icon" href="favicon.ico" /> </head> <body> <div id="notifier"></div> <button class="btn-1">Be amazing</button> <script src="index.js" /> </body> </html>
index.js
document.addEventListener('mouseleave', () => { document.querySelector('#notifier').textContent = "Don't leave me!"; });

The mouseleave event fires on an Element when the mouse exits its bounds.

You can also use document.body to listen for when the user exits the window, but the body needs to be as big as the viewport for this to work:

index.js
document.addEventListener('mouseleave', () => { document.querySelector('#notifier').textContent = "Don't leave me!"; });
index.html
<!DOCTYPE html> <html> <head> <title>Coding Beauty Tutorial</title> <link rel="icon" href="favicon.ico" /> <style> /* Makes body as big as viewport */ html, body { margin: 0; height: 100%; width: 100%; } </style> </head> <body> <h2>Welcome fellow developer</h2> <div id="notifier"></div> <script src="index.js"></script> </body> </html>

Using document.body over document could be better for compatibility with later Firefox versions.

Detect when user about to exit webpage

This helps to display an exit-intent popup, usually shown when the user is about to close the webpage or go to another.

Since tabs are usually at the top of the browser, we’d detect when the mouse leaves the browser window, but only from the top:

index.js
document.addEventListener('mouseleave', (event) => { if (event.clientY <= 0) { document.querySelector('#notifier').textContent = 'Have a good day!'; } });

clientY will be 0 at the very top of the viewport, so any higher and our if block’s code runs.

index.html
<!DOCTYPE html> <html> <head> <title>Coding Beauty Tutorial</title> <link rel="icon" href="favicon.ico" /> </head> <body> <h2 id="notifier"></h2> <div> Welcome to Coding Beauty, a site for all things coding. </div> <script src="index.js" /> </body> </html>

Create React App alternative: 5 times leaner, 0 vulnerabilities

Create React App is gone.

Yes, it’s no longer the recommended tool for starting React apps. Some of us were surprised, but many of us knew it was a long time coming.

There are much faster and leaner alternatives available today.

Why shouldn’t you use Create React App anymore?

Create React App was great.

Setting up React apps become as easy as ever without any need to bother with tedious Webpack and Babel configs. But despite the convenience, it had notable issues we just couldn’t ignore:

1. Bulky and slow

Create React App is a bloated tool.

It installs an unbelievable number of packages and takes forever to finish. If you’re a long-time CRA user like me, you’ve probably gotten used to this process, but it’s far from normal.

Once you enter the command to start a new project, you will quickly realize the insane number of packages CRA needs to set up.

Look at how large the node_modules folder is for CRA compared to Vite, a superior alternative. And both folders were just newly set up.

Many packages mean longer install times, even if you’re using PNPM or Yarn 2.

2. Insecure and outdated packages

Bundling so many packages makes it harder to keep track of them all, and many become abandoned and insecure later.

Things improved but any insecure package is something to worry about, and there are still quite a few.

Create React App has 8 high security vulnerabilities.
8 high security vulnerabilities! And that’s even better than what it used to be.

In my GitHub repos using CRA, I get several Dependabot alerts at every turn – even from one of my first GitHub projects.

And they’re almost always about security flaws in packages CRA installed.

Security weaknesses found in npm packages installed during Create React App setup.

3. Limited customization options

Create React App is a black box.

To add new libraries and customize it you usually have to depend on libraries like Craco – heck, before CRA version 5, Craco was pretty much our only way to get Tailwind CSS support.

I remember my earlier days of creating various Electron + React desktop apps, where I had to install react-app-rewired just to set the target to 'electron-renderer' to access Node modules in the React part of the app.

For total control, you can always do npm eject, but of course, then you become responsible for all the dependencies installed by CRA, which has trade-offs that may not be worth it for you.

What to use instead of Create React App

Old habits die hard, sure.

If you’ve been hesitating to switch to a superior tool, hopefully, the React team dumping CRA should finally do it for you. There are far more modern and better-maintained alternatives out there; let’s check out some of the most popular ones:

Vite

It’s a fast way to build modern web projects, designed for efficiency and smoother development.

The VueJS team is responsible for developing and maintaining Vite, so you can trust it’s a reliable tool.

  • “Extremely fast” Hot Module Replacement (HMR).
  • Out-of-the-box support for TypeScript.
  • A lightning-fast SWC compiler.

HMR means that code changes show instantly in the browser, avoiding full-page refreshes and saving valuable time.

It provides just about everything CRA does – but better and without the bloat. And it’s constantly being updated.

Vite logo

Here you don’t worry about outdated dependencies or vulnerable packages:

The Vite project has zero vulnerabilities.
Zero vulnerabilities.

Parcel

It’s a zero-config bundler with no setup required, yet it’s still more flexible than Create React App.

Parcel supports all the latest web technologies, including React, Vue, and TypeScript, and it can bundle any type of asset, from images and CSS to HTML and JavaScript.

Plus, it has excellent performance, thanks to its use of worker threads, which allows it to parallelize tasks and speed up builds.

Parcel logo

Parcel also has HMR support and is updated often with the latest features and optimizations like Vite. You can trust the team to keep up with the latest web technologies and best practices.

And what’s the result: your projects are built with the best tools available.

Custom Webpack configuration

Webpack is still king if you know what you’re doing.

The level of control of flexibility it gives you is unmatched as there are a massive number of options you can set, far more than just path and filename.

Another benefit to note: Webpack can generate a development build without a local server, unlike Parcel and others.

This particular feature saved me when I was debugging React in a VSCode extension. Loading data from localhost didn’t work properly for a Webview; I had to build to a local file and read the contents.

With source maps enabled and dev mode turned on, I could easily see the exact line where the error occurred in the TS code. Unlike with Parcel, where all I got was a minified build.

Of course, setting up a Webpack config can be complex and more involved – which is why CRA came around in the first place. You must configure various options to get everything working perfectly.

But once you’re set-up, you’ll have a heavily personalized build process for your project. It’s a trade-off that may or may not be worth it for you.

Frameworks to replace Create React App

Need React’s state and UI capabilities without extras? The tools we’ve seen are great.

Want high-performance apps with advanced features like SSR? Then consider frameworks with built-in routing, data fetching, etc.

Frameworks like:

Next.js

Many of us already know Next.js with advanced features like server-side rendering and static website generation.

Server-side rendering renders pages on the server and sends them to the client, greatly boosting performance, user experience, and SEO scores.

With intuitive file-based routing and built-in image optimization, Next.js is a smart option for building high-flying web apps with speed.

Remix

Another SSR-enable framework with powerful data features like:

  • Intelligent network error handling
  • Parallel fetching of CSS, JavaScript, and other assets to save time
  • Automatically ensuring the UI data is in sync with the server data after a user action.

One stand-out Remix feature is nested routes, which makes it possible to associate a specific section of a page with a particular route segment so that only that section updates when the route segment changes. This accelerates page transitions as no time is wasted re-rendering the entire page.

Gatsby

Gatsby is another awesome one – for scalable & fast-loading static sites.

There are a variety of starter kits and plugins to easily extend your site’s functionality and build a blog, e-commerce site, or portfolio site in no time at all.

With Gatsby, it’s effortless to power up your app with data from a CMS, especially with the GraphQL data layer for rapid integration with various APIs and services.

Final thoughts

CRA was a popular tool.

But it had its issues. It was bulky, outdated, and had limited customization.

Adaptation is crucial in the dynamic landscape of web development. By embracing modern tools, such as Vite, and Parcel, and in-depth frameworks like Next.js, Remix, and Gatsby, we can build faster and more efficient React apps.