JavaScript Essential for React

React: The library for web and native user interfaces

Destructing Objects and Arrays

const numbers = [10, 20, 30];

// Without destructuring
const first = numbers[0];
const second = numbers[1];

// With destructuring
const [a, b] = numbers;

console.log(a); // 10
console.log(b); // 20
const person = {
  name: "Alice",
  age: 25,
  city: "Taipei"
};

// Without destructuring
const name1 = person.name;
const age1 = person.age;

// With destructuring
const { name, age } = person;

console.log(name); // Alice
console.log(age);  // 25

Rest/Spread Operator

Spread Operator (...)

Expands (spreads) an array or object into individual elements.

const nums = [1, 2, 3];
const moreNums = [0, ...nums, 4, 5];

console.log(moreNums); // [0, 1, 2, 3, 4, 5]

function sum(a, b, c) {
  return a + b + c;
}

const numbers = [10, 20, 30];
console.log(sum(...numbers)); // 60

Rest Operator (...)

Collects multiple elements into a single array (or object).

const [first, ...rest] = [1, 2, 3, 4, 5];

console.log(first); // 1
console.log(rest);  // [2, 3, 4, 5]

const { name, ...otherInfo } = { name: "Alice", age: 25, city: "Taipei" };

console.log(name);      // Alice
console.log(otherInfo); // { age: 25, city: "Taipei" }

function sumAll(...numbers) {
  return numbers.reduce((acc, num) => acc + num, 0);
}

console.log(sumAll(1, 2, 3, 4)); // 10

Template Literals

const name = "Alice";
const age = 25;

const intro = `My name is ${name} and I am ${age} years old.`;
console.log(intro);
// My name is Alice and I am 25 years old.

const a = 10, b = 20;
console.log(`The sum is ${a + b}`); 
// The sum is 30


Multi-line Strings

No need for \n or string concatenation:

const poem = `
Roses are red,
Violets are blue,
JavaScript is fun,
And so are you!
`;

console.log(poem);

Ternaries Instead of If/else Statements

The ternary operator is a shorthand for an if/else statement.
Syntax:

condition ? expressionIfTrue : expressionIfFalse

let age = 20;
let status;

if (age >= 18) {
  status = "Adult";
} else {
  status = "Minor";
}

console.log(status); // Adult

let age = 20;
let status = age >= 18 ? "Adult" : "Minor";

console.log(status); // Adult

const score = 85;

const grade =
  score >= 90 ? "A" :
  score >= 80 ? "B" :
  score >= 70 ? "C" :
  "F";

console.log(grade); // B

Arrow Function

An arrow function is a shorter way to write a function, introduced in ES6 (2015).
It uses the => syntax.

function add(a, b) {
  return a + b;
}

const add = (a, b) => a + b;

console.log(add(2, 3)); // 5

const greet = (name) => {
  const message = `Hello, ${name}`;
  return message;
};

const person = {
  name: "Alice",
  greet: function() {
    setTimeout(() => {
      console.log(`Hi, I'm ${this.name}`);
    }, 1000);
  }
};

person.greet(); // Hi, I'm Alice

Short-Circuiting And Logical Operators

The main ones are:

  • || (OR)
  • && (AND)
  • ! (NOT)
  • ?? (Nullish coalescing, added in ES2020)
console.log(true || false);  // true
console.log(false || "Hello"); // "Hello"
console.log(0 || "Default"); // "Default"

let user = "";
let username = user || "Guest";
console.log(username); // Guest

console.log(true && "Hi");   // "Hi"
console.log(false && "Hi");  // false
console.log(0 && "Hello");   // 0

let isLoggedIn = true;
isLoggedIn && console.log("Welcome back!");

// Logs only if isLoggedIn is true
console.log(!true);  // false
console.log(!0);     // true

console.log(!!"Hello"); // true
console.log(!!0);       // false

console.log(null ?? "Default");   // "Default"
console.log(undefined ?? "Guest"); // "Guest"
console.log(0 ?? 100);  // 0  (since 0 is not null/undefined)

let count = 0;
console.log(count || 10); // 10   (bad — treats 0 as falsy)
console.log(count ?? 10); // 0    (good — 0 is valid)

Summary Table

OperatorReturnsExample
`` (OR)
&& (AND)First falsy value (or last if all truthy)1 && "Hi""Hi"
! (NOT)Inverts truthiness!0true
?? (Nullish)Right side only if left is null/undefined0 ?? 100

Optional Chaning

Optional chaining (?.) lets you safely access nested properties of an object without throwing an error if something is null or undefined.

const user = {};
console.log(user.profile.email); 
// ❌ TypeError: Cannot read property 'email' of undefined

console.log(user?.profile?.email); 
// ✅ undefined (no error)

object?.property
object?.[expression]
object?.method?.(args)

const person = {
  name: "Alice",
  address: { city: "Taipei" }
};

console.log(person?.address?.city); // Taipei
console.log(person?.contact?.phone); // undefined (safe!)

const user = {
  sayHi() { return "Hi!"; }
};

console.log(user.sayHi?.());   // Hi!
console.log(user.sayBye?.());  // undefined (no error)

const arr = [1, 2, 3];
console.log(arr?.[1]); // 2
console.log(arr?.[5]); // undefined

const city = person && person.address && person.address.city;

const city = person?.address?.city;

Combining with Nullish Coalescing (??)

Optional chaining returns undefined if the property doesn’t exist, so you can give defaults with ??

const user = {};
const email = user?.profile?.email ?? "No email provided";

console.log(email); // "No email provided"

Array map

map() is an array method in JavaScript.

It creates a new array by applying a function to each element of the original array.

It does not change the original array.

array.map(callback(currentValue, index, array), thisArg);

callback → function applied to each element.

currentValue → current element being processed.

index (optional) → index of current element.

array (optional) → the whole array.

thisArg (optional) → value of this inside callback.

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2);

console.log(doubled); // [2, 4, 6, 8]
console.log(numbers); // [1, 2, 3, 4] (unchanged)

const names = ["Alice", "Bob", "Carol"];
const withIndex = names.map((name, index) => `${index + 1}. ${name}`);

console.log(withIndex);
// ["1. Alice", "2. Bob", "3. Carol"]

const users = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 30 }
];

const namesOnly = users.map(user => user.name);

console.log(namesOnly); // ["Alice", "Bob"]

const prices = ["$10", "$20", "$30"];
const numbers = prices.map(price => Number(price.slice(1)));
console.log(numbers); // [10, 20, 30]

map() = transform each element of an array → new array.

Cleaner than writing a for loop.

Very powerful when working with arrays of objects, API responses, or rendering UI lists.

Array filter

filter() is an array method in JavaScript.

It creates a new array containing only the elements that pass a condition (the callback returns true).

It does not change the original array.

array.filter(callback(currentValue, index, array), thisArg);

callback → function that decides whether to keep each element (true) or discard it (false).

currentValue → the current element being processed.

index (optional) → index of current element.

array (optional) → the original array.

thisArg (optional) → value of this inside callback.

const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter(num => num % 2 === 0);

console.log(evens);   // [2, 4, 6]
console.log(numbers); // [1, 2, 3, 4, 5, 6] (unchanged)

const names = ["Alice", "Bob", "Anna", "Carol"];
const aNames = names.filter(name => name.startsWith("A"));

console.log(aNames); // ["Alice", "Anna"]

const users = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 17 },
  { name: "Carol", age: 30 }
];

const adults = users.filter(user => user.age >= 18);

console.log(adults);
// [ { name: "Alice", age: 25 }, { name: "Carol", age: 30 } ]

const mixed = [0, "Hello", "", null, 42, undefined];
const truthyOnly = mixed.filter(Boolean);

console.log(truthyOnly); // ["Hello", 42]

filter() = keep only elements that match a condition.

Perfect for searching, cleaning, or narrowing down arrays.

Often used together with map() for data transformation.

Array reduce

reduce() reduces an array to a single value.

It applies a function (called the reducer) to each element, carrying an accumulator along the way.

Very flexible: can be used for sums, averages, flattening arrays, grouping, etc.

array.reduce(callback(accumulator, currentValue, index, array), initialValue);

accumulator → result carried over between iterations.

currentValue → current element.

index (optional) → index of current element.

array (optional) → the whole array.

initialValue (optional but recommended) → starting value of the accumulator.

const numbers = [1, 2, 3, 4];

const sum = numbers.reduce((acc, num) => acc + num, 0);

console.log(sum); // 10

Explanation:

  • Start with acc = 0
  • Step 1: 0 + 1 = 1
  • Step 2: 1 + 2 = 3
  • Step 3: 3 + 3 = 6
  • Step 4: 6 + 4 = 10
//Example: Find Max
const nums = [10, 45, 2, 99, 32];

const max = nums.reduce((acc, num) => (num > acc ? num : acc), nums[0]);

console.log(max); // 99

//Example: Flatten an Array
const nested = [[1, 2], [3, 4], [5]];

const flat = nested.reduce((acc, arr) => acc.concat(arr), []);

console.log(flat); // [1, 2, 3, 4, 5]

//Example: Count Occurrences

const fruits = ["apple", "banana", "apple", "orange", "banana", "apple"];

const counts = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {});

console.log(counts);
// { apple: 3, banana: 2, orange: 1 }

//Example: Average
const scores = [80, 90, 100];

const avg = scores.reduce((acc, score, _, arr) => acc + score / arr.length, 0);

console.log(avg); // 90

reduce() = turn an array into one value.

Common uses: sum, max/min, flattening, grouping, counting, averages.

Looks harder at first, but it’s one of the most powerful tools in JavaScript.

Array sort

sort() is an array method that sorts the elements of an array in place (it modifies the original array).

array.sort([compareFunction])

compareFunction (optional) → defines sort order.

const numbers = [10, 2, 30, 25];

console.log(numbers.sort()); 
// [10, 2, 25, 30] ❌ (lexicographic order!)
//Problem: sort() converts numbers to strings by default.
// Ascending
numbers.sort((a, b) => a - b);
console.log(numbers); // [2, 10, 25, 30]

// Descending
numbers.sort((a, b) => b - a);
console.log(numbers); // [30, 25, 10, 2]

Compare function logic:

  • a - b < 0a comes before b
  • a - b > 0b comes before a
//Sorting Strings
const names = ["Alice", "Bob", "Carol", "anna"];
names.sort();
console.log(names); // ["Alice", "Bob", "Carol", "anna"] (capital letters first)

names.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
console.log(names); // ["Alice", "anna", "Bob", "Carol"]


//Sorting Objects
const users = [
  { name: "Alice", age: 25 },
  { name: "Bob", age: 30 },
  { name: "Carol", age: 20 }
];

// Sort by age ascending
users.sort((a, b) => a.age - b.age);

console.log(users);
/*
[
  { name: "Carol", age: 20 },
  { name: "Alice", age: 25 },
  { name: "Bob", age: 30 }
]
*/

sort() = rearrange array elements.

Default: lexicographic sort → may be unexpected for numbers.

Use a compare function for numbers or custom rules.

Mutates the original array → use [...array].sort() if you want a copy.

Working With Immutable Array

What is an Immutable Array?

This is very important in React or functional programming to avoid unintended side effects.

Immutable means you do not change the original array.

Instead of modifying it (like push, pop, splice), you create a new array with the desired changes.

Why Use Immutable Arrays?

  • Prevent bugs caused by accidental mutations
  • Make state management predictable
  • Enable undo/redo or time-travel debugging
//Adding Elements
const arr = [1, 2, 3];

// Add to the end
const newArr1 = [...arr, 4];
console.log(newArr1); // [1, 2, 3, 4]

// Add to the start
const newArr2 = [0, ...arr];
console.log(newArr2); // [0, 1, 2, 3]

//Removing Elements
const arr = [1, 2, 3, 4];

// Remove element with filter
const newArr = arr.filter(num => num !== 2);
console.log(newArr); // [1, 3, 4]

//Updating Elements
const arr = [1, 2, 3, 4];

// Increment all numbers by 1
const newArr = arr.map(num => num + 1);
console.log(newArr); // [2, 3, 4, 5]

//Combining Arrays
const arr1 = [1, 2];
const arr2 = [3, 4];

const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4]

Immutable arrays = never modify the original array

Always return a new array for additions, deletions, or updates

Essential in React state updates and functional programming

Asynchronous Promise

A Promise is a JavaScript object that represents the eventual completion or failure of an asynchronous operation.

  • Think of it as a placeholder for a value that will be available in the future.
  • Promises help avoid callback hell and make async code easier to read.

A Promise has three states:

  1. Pending – initial state, operation not finished yet
  2. Fulfilled – operation completed successfully
  3. Rejected – operation failed
const myPromise = new Promise((resolve, reject) => {
  const success = true; // simulate async operation
  if (success) {
    resolve("Operation succeeded!");
  } else {
    reject("Operation failed!");
  }
});

resolve(value) → fulfills the promise

reject(error) → rejects the promise

myPromise
  .then(result => {
    console.log(result); // "Operation succeeded!"
  })
  .catch(error => {
    console.log(error);
  });

.then() → runs if promise is fulfilled

.catch() → runs if promise is rejected

new Promise((resolve, reject) => {
  resolve(2);
})
.then(num => num * 2)
.then(num => num + 1)
.then(result => console.log(result)); // 5

Each .then() receives the result of the previous one

Makes async code readable and sequential

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = { id: 1, name: "Alice" };
      resolve(data);
    }, 1000);
  });
}

fetchData()
  .then(data => console.log(data))
  .catch(err => console.error(err));

Promise.all() and Promise.race()

  • Promise.all([p1, p2, ...]) → waits for all promises to resolve
  • Promise.race([p1, p2, ...]) → resolves/rejects as soon as one finishes
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);

Promise.all([p1, p2]).then(results => console.log(results)); // [1, 2]

Promise = object representing future result of async operation

States: pending, fulfilled, rejected

Use .then() for success, .catch() for errors

Chain promises for sequential operations

Useful for API calls, file reading, timers, etc.

Asynchronous Async/Await

async and await are syntactic sugar on top of Promises.

They allow you to write asynchronous code that looks synchronous, making it easier to read and maintain.

async function greet() {
  return "Hello!";
}

greet().then(msg => console.log(msg)); // Hello!

Declaring a function with async makes it always return a Promise.

Even if you return a normal value ("Hello!"), it’s automatically wrapped in a Promise.

function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function run() {
  console.log("Start");
  await wait(1000); // wait 1 second
  console.log("End"); 
}

run();
// Start
// (1 second delay)
// End

await pauses execution until the Promise resolves.

Can only be used inside async functions.

async function fetchUser() {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
    const data = await response.json();
    console.log(data);
  } catch (err) {
    console.error("Error:", err);
  }
}

fetchUser();

Sequential (waits one by one):

async function sequential() {
  const a = await Promise.resolve(1);
  const b = await Promise.resolve(2);
  console.log(a + b); // 3
}

Parallel (run at the same time):

async function parallel() {
  const [a, b] = await Promise.all([
    Promise.resolve(1),
    Promise.resolve(2)
  ]);
  console.log(a + b); // 3
}

async → marks a function as asynchronous (returns a Promise)

await → waits for a Promise to resolve inside an async function

Use try/catch for error handling

Can combine with Promise.all() for parallel operations

Spread the love

Leave a Reply

Your email address will not be published. Required fields are marked *