Essential Javascript Review Before Starting React Development

Essential Javascript Review Before Starting React Development

As the landscape of web development evolves, particularly with the shift from traditional backend technologies to the more dynamic React ecosystem, a robust understanding of JavaScript's foundational concepts becomes crucial.

This blog, "Essential JavaScript Review," is designed to refresh and deepen your grasp of key JavaScript features that are integral to proficient React development.

Whether you are a seasoned backend developer venturing into modern JavaScript frameworks or simply refining your programming prowess, this guide serves as your gateway into the vibrant world of React.

Here, we revisit essential topics like variables and scope, delve into the functionality of objects and arrays, explore the nuances of destructuring, and unlock the expressive power of template literals. Each section is designed to not only make your transition into React smoother but also to enhance your overall programming skills, laying a solid foundation for more advanced topics.

1 - Variables & Scope

Previously, var was the only keyword available for declaring variables in JavaScript. However, the introduction of ES6 brought two new keywords, const and let, which have since become staples in modern JavaScript development, including React applications.

let and const are preferred over var due to their block-scoped nature, which makes the code more predictable and reduces the likelihood of errors.

Specifically, const is favored for declaring components and other entities in React that are not meant to be reassigned, thus enhancing code stability and readability.

Here’s a simple demonstration of how var, let, and const differ in terms of scope:

if(true){
  var var1 = "Declaring var1 using var keyword!"
  let var2 = "Declaring var2 using let keyword!"
  const var3 = "Declaring var3 using const keyword!"
}
console.log(var1); //Outputs: "Declaring var1 using var keyword!" - as var is function or globally scoped
console.log(var2); //Uncaught ReferenceError as let is block scoped
console.log(var3); //Uncaught ReferenceError as const is block scoped

This example highlights the differences in scoping between var, let, and const. It shows how ES6 has improved error prevention and the structure of programs.

2 - Ternary Operator

The ternary operator in JavaScript, also known as the Conditional Operator, is a concise, three-operand alternative to the if statement which evaluates a condition and returns one of two values.

Ternary operator is structured as - conditioncheck ? valueIfTrue : valueIfFalse

In the React world, this is very handy if you want to use: if-else logic within the returned JSX.

Here’s how it can be employed to determine a simple condition:

const age = 21;
const access = age >= 21 ? 'granted' : 'denied';
console.log(access); 
// Outputs: "granted"

3 - Template Literals

Introduced in ES6, template literals have revolutionized the way we construct strings in JavaScript. By eliminating the cumbersome process of string concatenation, template literals allow for the seamless inclusion of variables and expressions directly within strings.

How to Create Template Literals: To create a template literal, simply use backticks (`) instead of the traditional single (' ') or double (" ") quotes.

Incorporating Expressions and Variables: Within template literals, expressions and variables can be embedded directly by enclosing them in ${}. This feature is particularly powerful for creating dynamic content right within the string's text.

Consider the following scenario where we want to display product information dynamically:

const product = {
  name: "iPhone 15 Pro",
  price: 2099,
  discount: 0.2, // 20% discount
};

console.log(
  `The ${product.name} is originally priced at $${
    product.price
  }, but with the current discount of ${
    product.discount * 100
  }%, it's now available 
for $${product.price - product.price * product.discount}!`
);
//Outputs: The iPhone 15 Pro is originally priced at $2099, but with the current discount of 20%, it's now available 
// for $1679.2!

In this example, template literals allow us to integrate variables and calculations directly into the string, displaying a tailored message without the need for tedious '+' operators or complex concatenation logic.

4- Destructuring - Objects

Object destructuring in JavaScript is a powerful feature that simplifies the way we access properties from objects, particularly when working with the state or props in a component, a common pattern in React development.

Efficiency of Object Destructuring: Rather than accessing properties one by one, object destructuring allows you to extract multiple properties from an object simultaneously and assign them to variables. This not only makes the code cleaner but also enhances its readability.

Here is how you can utilize object destructuring in a practical scenario:

const sugPresentation = {
  topic: "Traditional Sitecore Developer's Guide to Composable Architecture",
  presenter: "Saurabh",
  venue: "Datacom,Auckland",
};

//Usage of Object destructuring to fetch data out of an Object
const { topic, presenter} = sugPresentation;
console.log(`${topic} will be presented by ${presenter}`);
//Output-Traditional Sitecore Developer's Guide to Composable Architecture will be presented by Saurabh

As shown in above code snippet - it's important that we give the variables - topic, presenter - the exact same names as the properties in the object.

5 - Destructuring - Arrays

Array destructuring in JavaScript provides a straightforward way to access items from an array. This technique is particularly useful for directly extracting elements without accessing them through their indices.

How Array Destructuring Works: Similar to object destructuring, array destructuring allows you to unpack values from an array. However, instead of using property names, array destructuring is based on the position of elements in the array. We use square brackets [], which reflect the array's syntax, to perform destructuring.

Consider an array representing different products in the Sitecore Content Cloud:

const sitecoreContentCloudProducts = [
  "XM Cloud",
  "Search",
  "Content Hub ONE",
  "Content Hub DAM",
  "Content Hub Operations",
];
const [product1, product2] =
  sitecoreContentCloudProducts;

console.log(
  `Sitecore Content Cloud Product Names : ${product1} , ${product2}`
);
//Output : Sitecore Content Cloud Product Names : XM Cloud , Search
//Outputs the first 2 elements from array - sitecoreContentCloudProducts

In this example, product1 and product2 are extracted from sitecoreContentCloudProducts. The first element in the array, "XM Cloud", is assigned to product1, and the second, "Search", to product2. This method of accessing array elements simplifies the process, making the code cleaner and easier to understand.

While array destructuring is highly efficient for accessing elements, it's important to consider that skipping elements and default values can also be handled with this syntax. For example, if you want to skip certain elements, you can leave blanks in the destructuring assignment.

const [first, , third] = ["item1", "item2", "item3"];
console.log(first, third); // Outputs: item1, item3

6- Rest Operator

The Rest operator, represented by three dots ..., is a versatile feature in JavaScript that simplifies the process of handling leftover items when destructuring arrays or objects. This operator is invaluable for grouping the remaining parts of an array or object into a new array or object.

Usage in Arrays: When used with arrays, the Rest operator collects all elements beyond those that have been destructured into variables.

Consider the following array of Sitecore Content Cloud products:

const sitecoreContentCloudProducts = [
  "XM Cloud",
  "Search",
  "Content Hub ONE",
  "Content Hub DAM",
  "Content Hub Operations",
];
const [product1, product2, ...remainingProducts] = sitecoreContentCloudProducts;
console.log(remainingProducts); 
// Output: ['Content Hub ONE', 'Content Hub DAM', 'Content Hub Operations']

In this example, product1 and product2 capture the first two elements, while remainingProducts gathers the rest. The Rest operator allows us to efficiently manage and utilize the remaining elements without manually indexing each one.

Usage in Objects: Similarly, when destructuring objects, the Rest operator helps in extracting the remaining properties not captured by the assigned variables.

Let's apply the Rest operator to an object representing a presentation:

const sugPresentation = {
  topic: "Traditional Sitecore Developer's Guide to Composable Architecture",
  presenter: "Saurabh",
  venue: "Datacom,Auckland",
  date: "25th May, 2024",
  presentationMode: "in-person",
};
const { topic, presenter, ...otherPresentationDetails } = sugPresentation;
console.log(otherPresentationDetails);
//Output- {venue: 'Datacom,Auckland', date: '25th May, 2024', presentationMode: 'in-person'}

Here, topic and presenter are extracted, and otherPresentationDetails encompasses all other properties, demonstrating a clean and efficient way to segregate specific properties from others.

7- Spread Operator

The Spread operator, denoted by same three dots ... like rest , is a powerful feature in JavaScript extensively used in both array and object manipulations.

It allows for the expansion of an array's elements or an object's properties into a new context, which is useful in React development.

Using the Spread Operator with Arrays: The Spread operator can be used to create new arrays by combining and adding elements from existing arrays.

This is particularly useful for operations where you need to merge several arrays or add new elements without mutating the original arrays.

Consider we have an array of products and we want to add more items to it:

const sitecoreContentCloudProducts = [
  "XM Cloud",
  "Search",
  "Content Hub ONE",
  "Content Hub DAM",
  "Content Hub Operations",
];

const sitecoreComposableProducts = [
  ...sitecoreContentCloudProducts,
  "CDP",
  "Personalize",
];

console.log(sitecoreComposableProducts);
//Output-  ['XM Cloud', 'Search', 'Content Hub ONE', 'Content Hub DAM', 'Content Hub Operations', 'CDP', 'Personalize']

This example demonstrates how the Spread operator can efficiently combine elements from an existing array with new ones, enhancing flexibility in data manipulation.

Using the Spread Operator with Objects: Similarly, the Spread operator simplifies the creation of new objects by enabling the merging of properties from existing objects, which is especially useful in React for managing state updates or configurations.

Let's look at how properties from one object can be spread into another, allowing for updates and additions:

const sugPresentationInitialNotification = { 
    venue: "Datacom,Auckland",
    date: "25th May, 2024",
    presentationMode: "in-person",
  };

const updatedSugPresentationDetails ={
    ...sugPresentationInitialNotification,
    date:"26th May, 2024", 
    topic: "Introduction to Sitecore XM Cloud Forms",
    presenter: "Saurabh",
}

console.log(sugPresentationInitialNotification);
//Output: {venue: 'Datacom,Auckland', date: '25th May, 2024', presentationMode: 'in-person'}

console.log(updatedSugPresentationDetails);
//{venue: 'Datacom,Auckland', date: '26th May, 2024', presentationMode: 'in-person', topic: 'Introduction to Sitecore XM Cloud Forms', presenter: 'Saurabh'

In this example, the Spread operator allows us to seamlessly integrate and update object properties, showcasing its effectiveness in creating dynamic and mutable data structures without directly altering the original object.

The Spread operator’s ability to efficiently handle arrays and objects ensures that developers can maintain immutability where necessary while still performing direct manipulations when needed. Its utility in React, especially for state management, makes it an essential tool for modern web developers.

8- Arrow Functions

Introduced in ES6, arrow functions provide a more concise way to write functions in JavaScript.

They are especially useful for their short form and the way they handle the this context, which is very helpful in situations with callbacks and methods where it's important to keep the context of this.

Arrow functions remove the need for the function keyword and simplify function declaration.

Here’s a closer look at how they work, compared to traditional ES5 functions:

Traditional ES5 Function Example:

const sugPresentation = {
  venue: "Datacom,Auckland",
  date: "25th May, 2024",
};

// Traditional  ES5 function to format presentation details into a summary
function formatSugPresentationES5(presentation) {
  return (
    "Upcoming Sitecore User group will be hosted at " +
    presentation.venue +
    " on " +
    presentation.date
  );
}

var presentationSummary = formatSugPresentationES5(sugPresentation);
console.log(presentationSummary);
//Output : Upcoming Sitecore User group will be hosted at Datacom,Auckland on 25th May, 2024

Let's understand Arrow functions using code snippets below, comparing it with ES5 functions and also covering- Arrow functions with a body and explicit return statement versus an Arrow function without a body along with an implicit return.

Arrow Function with Explicit Return:

Arrow functions can also include an explicit return statement when multiple operations need to be performed:


//Arrow Function with Explicit Return
const formatSugPresentationUsingEs6ArrowFunctionAndExplicitReturn = (
  presentation
) => {
   // Additional processing can be done here
  return `Upcoming Sitecore User group will be hosted at ${presenation.venue} on ${presentation.date}`;
};
const presentationSummary1 =
  formatSugPresentationUsingEs6ArrowFunctionAndExplicitReturn(sugPresentation);
console.log(presentationSummary1);
//Output : Upcoming Sitecore User group will be hosted at Datacom,Auckland on 25th May, 2024

Arrow Function with Implicit Return:

When an arrow function contains only a single expression, you can omit both the return keyword and the curly braces {}, making the function body even more concise.

//Arrow Function with Implicit Return
const formatSugPresentationUsingEs6ArrowFunctionAndImplicitReturn = (
  presentation
) =>
  `Upcoming Sitecore User group will be hosted at ${presentation.venue} on ${presentation.date}`;
const presentationSummary2 =
  formatSugPresentationUsingEs6ArrowFunctionAndImplicitReturn(sugPresentation);
console.log(presentationSummary2);
//Output : Upcoming Sitecore User group will be hosted at Datacom,Auckland on 25th May, 2024

9- Essential Array methods in JS

  1. Array map method

    The map method in JavaScript processes each element in an array and returns a new array of the same length, where each element has been transformed based on a specified function. This characteristic makes the map method ideal for scenarios where you need to apply transformations to each item in an array without altering the original data.

    Functionality of the Map Method: The map method takes a callback function that is executed on each element of the array. This callback allows you to access each element, perform operations on it, and return a new value that takes the element's place in the resulting array.

    Let's consider a practical example where we have an array of usergroup session objects and we need to extract just the session topics and presenter names to create a modified array:

     const sugSessions = [
       {
         venue: "Datacom",
         date: "5th July, 2023",
         presenter: "Jack Spektor",
         topic:
           "Unleashing the Power: From Monolith to Headless Sitecore and XM Cloud",
       },
       {
         venue: "Datacom",
         date: "5th July, 2023",
         presenter: "Mohamed Abdullah",
         topic: "Unify Your Customer Data with Sitecore CDP & Personalize",
       },
       {
         venue: "AKQA",
         date: "7th Dedember, 2023",
         presenter: "Tamas Varga",
         topic: "Sitecore Community and the MVP Program",
       },
     ];
    
     const sessionTopicsAndPresenters = sugSessions.map((session) => {
       return {
         topic: session.topic,
         presenter: session.presenter,
       };
     });
     // Output: Array of objects each containing the topic and presenter nam
    

    In this code snippet, the map method is used to iterate over the sugSessions array and extract the topic and presenter from each session into a new object. The resulting array, sessionTopicsAndPresenters, is a simplified version of the original, containing only the necessary details.

  2. Array filter method

    The filter method - is essential for extracting elements from an array that meet specific criteria. It provides a robust mechanism for conditional selection without altering the original array.

    How the Filter Method Works: Unlike the map method, which transforms elements, the filter method evaluates each element of the source array against a specified condition. It uses a callback function that returns a boolean value - true or false. Elements causing the callback function to return true are included in the new array, while those that return false are excluded.

    To demonstrate the utility of the filter method, let's apply it to the same array used in our map method example. We will filter sessions based on their venue:

     const sessionsHostedByDatacom = sugSessions.filter(
       (session) => session.venue === "Datacom"
     );
     // Output: Array containing only the sessions hosted at Datacom
    

    This example clearly shows how the filter method can be used to selectively retrieve array elements that meet a particular condition. In this case, only the sessions that are hosted at "Datacom" are included in the resulting array, sessionsHostedByDatacom.

  3. Array reduce method

    The reduce method is used for combining all elements in an array into a single result based on a function you provide.

    Unlike map and filter, which return arrays, reduce can return any value type -whether it's a number, string, object, or even another array - making it extremely versatile.

    How the Reduce Method Works:

    Reduce operates by taking two parameters: a reducer function and an optional initial value.

    The reducer function is executed for each element in the array, receiving two arguments:

    • An accumulator, which holds the combined result up to the current iteration.

    • The current element being processed.

Lets look an example - where we sum an array of numbers:

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

    const sum = numbers.reduce((acc, currentValue) => {
      return acc+ currentValue;
    }, 0);  // initial value of the accumulator is 0.
    console.log(sum);  
    // Output: 15

In this example, reduce iteratively adds each number to the accumulator, which starts at zero, resulting in the total sum of the array elements.

Now, let's apply reduce to the same source array we used in the map and filter method examples - to count how many sessions are held at each venue.

    const venueCount = sugSessions.reduce((acc, session) => {
      acc[session.venue] = (acc[session.venue] || 0) + 1;
      return acc;
    }, {});

    console.log(venueCount);
    // Output: { 'Datacom': 2, 'AKQA': 1 }

Above example demonstrates how reduce can consolidate data into an object where keys represent venues and values represent the count of sessions at each venue, showcasing the method's capacity to handle sophisticated data aggregation tasks effectively.

  1. Array sort method

    The sort method in JavaScript is used to order the elements within an array according to a specified criterion. By default, the sort method treats elements as strings and arranges them based on their Unicode values. This default behavior is crucial to understand, especially when sorting data that isn't inherently textual.

    Default Behavior:

    When sorting an array of words, the default behavior is case-sensitive due to the reliance on Unicode values:

     const words = ['Banana', 'apple', 'Cherry', 'date'];
     words.sort();
     console.log(words);  
     // Output: ['Banana', 'Cherry', 'apple', 'date']
    

    In this example, uppercase letters are sorted before lowercase ones, illustrating the Unicode-based sorting mechanism.

    Sorting with Custom Comparator:

    For numerical values or custom sorting rules (like case-insensitive sorting), a comparator function can be provided:

    Numerical Sorting:

     const numbers = [5, 3, 9, 1, 4];
     numbers.sort((a, b) => a - b);
     console.log(numbers);
     // Output: [1, 3, 4, 5, 9]
    

    This code snippet demonstrates how to sort numbers in ascending order using a comparator that subtracts one number from another to determine their order.

    Sorting Objects:

    Sorting becomes more complex when arrays contain objects. You might need to sort these objects based on specific properties, such as dates:

     const sugSessions = [
       {
         venue: "Datacom",
         date: "2023-07-05",
         presenter: "Jack Spektor",
         topic: "Unleashing the Power: From Monolith to Headless Sitecore and XM Cloud",
       },
       {
         venue: "Datacom",
         date: "2023-07-03",
         presenter: "Mohamed Abdullah",
         topic: "Unify Your Customer Data with Sitecore CDP & Personalize",
       },
       {
         venue: "AKQA",
         date: "2023-12-07",
         presenter: "Tamas Varga",
         topic: "Sitecore Community and the MVP Program",
       },
     ];
    
     sugSessions.sort((a, b) => new Date(a.date) - new Date(b.date));
     console.log(sugSessions);
     // Outputs the sessions sorted by date
    

    This example shows how sessions can be sorted by date, making it easier to process events chronologically.

    Important Consideration: Remember, the sort method modifies the original array. To preserve the original order for other uses, consider making a copy of the array using the spread operator [...array] before sorting.

10- Promises and Async/Await

As JavaScript applications increase in complexity, especially in environments like React where fetching data from a server is a common task, understanding asynchronous programming is essential.

Promises and the async/await syntax are fundamental tools for effectively managing these types of operations.

Understanding Promises: A Promise in JavaScript represents the eventual completion or failure of an asynchronous operation.

It serves as a placeholder for a value that is initially unknown but will eventually be resolved (successfully completed) or rejected (failed).

Creating and Using Promises:

const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      try {
        resolve('Succesful Data fetch!!');
      } catch (error) {
        reject('Error fetching data!!');
      }
    }, 2000);
  });
};

fetchData()
  .then(data => console.log(data))  // Outputs: "Succesful Data fetch!!"
  .catch(error => console.error(error));

This snippet demonstrates how to encapsulate an asynchronous operation within a Promise, using setTimeout to simulate a delay.

Real-world Application Using Promises: Let's apply this concept to a real-world scenario using the JSONPlaceholder API:

fetch("https://jsonplaceholder.typicode.com/users")
  .then(response => response.json())
  .then(data => console.log(data));
// Output: Logs the JSON response containing user data

This example illustrates how Promises are used to handle HTTP requests asynchronously.

Simplifying with Async/Await:

The async/await syntax provides a cleaner and more intuitive way to work with Promises, allowing asynchronous code to be written in a more synchronous manner.

async function getUsers() {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/users");
    const users = await response.json();
    console.log(users);
  } catch (error) {
    console.error('Failed to fetch users:', error);
  }
}

getUsers();  // Logs the JSON response of users

This method simplifies the handling of Promises, reducing the complexity of chaining .then() methods and integrating error handling directly through try/catch blocks.

In wrapping up this essential review of JavaScript, we've traversed a spectrum of critical features and functionalities that pave the way for efficient React development.

Thank you for joining me in this exploration of JavaScript essentials - your next step is - Enhancing JavaScript with type safety, ensuring more reliable code via TypeScript.