Seamless Digital Experience.
Happy Customers.

Digital Experience and Error Monitoring Platform - Zipy

JavaScript concepts to know before learning React

Aryan Raj
~ 10 min read | Published on Jan 24, 2024





TABLEย OFย CONTENT

Fix bugs faster with Zipy!

  • Session replay
  • Network calls
  • Console Logs
  • Stack traces
  • User identification
Get Started for Free

Ready to get started with React, but not sure where to start? Before you embark on your React journey and figuring how you can deploy your React app, it's crucial to have a solid understanding of fundamental JavaScript concepts. Avoid the common pitfall of a 'learn as you go' approach and ensure you have a strong foundation in JavaScript variables, data types, conditional statements, loops, arrays, objects, and functions.

Get familiar with the latest ES6 syntax, like arrow functions, destructuring, and spread operators, and have a grasp on asynchronous programming techniques like Promises and async/await. We will also explore how understanding the Document Object Model (DOM) and manipulating it with JavaScript can give you an advantage when building React-based user interfaces.

Letโ€™s dive in these JavaScript concepts and set yourself up for success to learn React!

Take Javascript debugging to the next level, with Zipy!

Get Started for free

Callback functions in JavaScript

A callback function is a function that gets executed after the completion of another function. It is usually passed as an argument to another function.

It is essential to comprehend callbacks as they are widely used in various areas such as array methods like map(), filter(), etc., setTimeout(), event handlers such as click, scroll, and others.

A callback can either be a regular function or an arrow function. Arrow function is a concise way to write anonymous functions in JavaScript.

For instance, consider the following example of an event listener for the 'click' event with a callback function, which will be triggered every time the button is clicked:

//HTML
<button class="btn">Click Me</button>

//JavaScript
const btn = document.querySelector('.btn');

btn.addEventListener('click', () => {
let name = 'Zipy';
console.log(name.toUpperCase());
});

Promises in JavaScript

A callback function is executed after the main function completes its execution. Chaining multiple callback functions in a sequence provides the ability to regulate when a specific function should execute, whether it is after the parent function finishes its execution or after a specific time duration has elapsed.

The following code is an example of how to log five names in the console with a delay of 5 seconds between each name, using nested setTimeout functions. The first name is logged 5 seconds after the function is called, and subsequent names are logged with a 5-second interval between each.

setTimeout(() => {
  console.log("Alpha");
  setTimeout(() => {
    console.log("Beta");
    setTimeout(() => {
      console.log("Gamma");
      setTimeout(() => {
        console.log("Delta");
        setTimeout(() => {
          console.log("Epsilon");
        }, 5000);
      }, 5000);
    }, 5000);
  }, 5000);
}, 5000);

The example mentioned above will function as intended, but it can be challenging to understand, debug, or include error handling due to the complex nested callbacks. This situation is referred to as 'Callback Hell.' Callback Hell is a significant problem resulting from coding with intricately nested callbacks.

Promises are used to avoid the difficulties of Callback Hell. They allow asynchronous code to be written in a more straightforward, synchronous manner.

A promise is an object that represents a value that is expected to be available in the future, but is currently not available.

One common application of promises is in HTTP requests, where a request is sent and the response is not immediately received, as it is an asynchronous process. The result (either data or an error) is only obtained once the server sends a response.

Here is an example of the syntax for promises in JavaScript:

const myPromise = new Promise((resolve, reject) => {  
    // condition
});

Promises in JavaScript have two parameters, one for handling success (resolve) and the other for handling failure (reject).The Promise can only be resolved if the conditions specified in either the resolve or reject parameters are met; if not, the promise will be rejected.

const promise = new Promise((resolve, reject) => {  
    let condition;
    
    if(condition is met) {    
        resolve('Promise is resolved successfully.');  
    } else {    
        reject('Promise is rejected');  
    }
});

The promise object has three states:

  1. Pending: This is the initial state, before the promise has either succeeded or failed.
  2. Resolved: The promise has been successfully completed.
  3. Rejected: The promise has failed.

Lastly, let's see how the callback hell can be rewritten using a promise:

function addName (time, name){
  return new Promise ((resolve, reject) => {
    if(name){
      setTimeout(()=>{
        console.log(name)
        resolve();
      },time)
    }else{
      reject('No such name');
    }
  })
}

addName(5000, 'Alpha')
  .then(()=>addName(5000, 'Beta'))
  .then(()=>addName(5000, 'Gamma'))
  .then(()=>addName(5000, 'Delta'))
  .then(()=>addName(5000, 'Epsilon'))
  .catch((err)=>console.log(err))

This code defines a function called addName which accepts two parameters, time and name. This function returns a Promise that will either resolve after the specified time with the provided name parameter or reject with an error message of 'No such name' if no name is provided.

The Promise returned by addName is then used in a chain of .then() calls to log out a sequence of names with a delay of 5000 milliseconds between each name. The chain starts with addName(5000, 'Alpha') which will log out 'Alpha' after 5000 milliseconds, then continues with addName(5000, 'Beta') which will log out 'Beta' after another 5000 milliseconds, and so on.

If any of the Promises in the chain are rejected with an error message (i.e., if name is not provided to addName), then the chain will be broken and the error message will be logged out using the .catch() method.

Overall, this code demonstrates the use of Promises to perform asynchronous operations in a sequential manner.

Take Javascript debugging to the next level, with Zipy!

Get Started for free

Switch statement

In medium to large scale React applications, it's common to encounter the use of switch statements for managing states across components. To address this issue, tools such as the useReducer Hook or Redux are often utilized.

The example below demonstrates a reducer function that employs a switch statement for state management. It's worth noting that switch statements are not a requirement for use with reducers, but they are a widely accepted pattern.

export default (state, action) => {
  switch (action.type) {
    case "TOGGLE_DARK_MODE":
      return {
        ...state,
        darkMode: action.darkMode,
      };
     case "UPDATE_PLAYBACK": {
      return {
        ...state,
        currentSound: action.currentSound,
      };
    }  
    default:
      return state;
  }
};

This example evaluates the action.type value and executes the code of a corresponding case statement. If it evaluates to the string 'TOGGLE_DARK_MODE', then the code in the first case statement will be executed.

Having a default clause is considered best practice, as it will run if none of the case clauses matches the switch expression. The use of the spread operator (e.g. ...state) is also a common technique.

In the example, each case (including the default) returns a new object, which represents the updated state in React. This highlights an important concept in understanding ReactJs.

Map() in JavaScript

The Array.map() method is a commonly utilized approach that allows you to traverse an array and manipulate its elements using a callback function. The callback will be executed for each element in the array.Assume, we have an array of user information.

let users = [
  { firstName: "Sarah", lastName: "Jones", age: 18, profession: "Software developer", location: "New York" },
  { firstName: "Oliver", lastName: "Brown", age: 22, profession: "Product manager", location: "London" },
  { firstName: "Lila", lastName: "Garcia", age: 19, profession: "Customer success", location: "Barcelona" }
];

We can loop through, using a map and modify accordingly.

let singleUser = users.map((user)=>{
  // Let's add the first letter of the first name to the last name
  let userName = user.firstName.charAt(0) + user.lastName;
  return `
    <h3 class='name'>${userName}</h3>
    <p class="profession">${user.profession}</p>
    <p class="location">New York</p>
  `
});

It's important to understand that map() returns a new array and does not alter the size of the original array. It only uses the values from the original array to generate a new one.

One major use of a map is to convert data into HTML, or in the case of React, JSX. This allows you to encapsulate your data within a specific structure.

Filter() and Find() in JavaScript

The filter() method generates a new array based on specified criteria. It differs from map() in that it can modify the size of the resulting array. On the other hand, the find() method only returns a single instance, which could be an object or an item. If there are multiple matches, it will return the first one, while returning undefined if no matches are found. Here's an example of using the filter() method to create a new array of numbers that are greater than 5:

const numbers = [2, 6, 8, 3, 9, 4, 7];

const filteredNumbers = numbers.filter((number) => {
  return number > 5;
});

console.log(filteredNumbers); // Output: [6, 8, 9, 7]

In this example, we start with an array of numbers. We then use the filter() method to create a new array, filteredNumbers, that only includes the numbers that are greater than 5. The callback function passed to filter() takes each element of the array as its argument. It then returns true or false based on whether the element should be included in the new array or not. In this case, we only include the element if it is greater than 5. Finally, we log the new array to the console.

Find()

The find() method is similar to the filter() method in that it searches through an array to find an item that meets a specific condition. However, unlike the filter() method, the find() method stops the search as soon as it finds the first item that meets the condition and returns that item. If the method cannot find any item that satisfies the condition, it returns undefined.

Suppose we have an array of numbers called numbersArray:

const fruits = [
  { name: 'apple', color: 'red' },
  { name: 'banana', color: 'yellow' },
  { name: 'grape', color: 'purple' },
  { name: 'orange', color: 'orange' }
];

const foundFruit = fruits.find(fruit => fruit.color === 'purple');

console.log(foundFruit); // Output: { name: 'grape', color: 'purple' }

This code declares an array called fruits that contains four objects, each representing a different fruit with a name and a color. The find() method is then called on the fruits array with an arrow function as an argument that checks each fruit object's color property and returns the first fruit object that has a color property equal to 'purple'. The returned fruit object is assigned to a variable called foundFruit.

The console.log() function is then used to output the value of foundFruit, which is the first fruit object in the fruits array that has a color property of 'purple'. In this case, the output would be { name: 'grape', color: 'purple' }.

In summary, the find() method searches through an array and returns the first element that satisfies a provided condition. In this example, it returns the first fruit object that has a color property of 'purple'.

Object destructuring

The principle of object destructuring is pretty simple. With the elegant syntax below, we can extract properties into variables.

const creatures = {
  animals: ["๐Ÿถ", "๐Ÿฑ", "๐Ÿป", "๐Ÿผ", "๐Ÿฆ"],
  mythical: ["๐Ÿงœ‍โ™€๏ธ", "๐Ÿฆ„", "๐Ÿ‰", "๐Ÿ‘ป", "๐ŸงŸ‍โ™‚๏ธ"]
};

const { animals, mythical } = creatures;

console.log(animals); // ["๐Ÿถ", "๐Ÿฑ", "๐Ÿป", "๐Ÿผ", "๐Ÿฆ"]
console.log(mythical); // ["๐Ÿงœ‍โ™€๏ธ", "๐Ÿฆ„", "๐Ÿ‰", "๐Ÿ‘ป", "๐ŸงŸ‍โ™‚๏ธ"]

When you use assignment without variable declaration in JavaScript, you are required to use parentheses to avoid a syntax error.

const emojis = {
animals: ["๐Ÿถ", "๐Ÿฑ", "๐Ÿญ", "๐Ÿน", "๐Ÿฐ"],
food: ["๐Ÿ•", "๐Ÿ”", "๐ŸŸ", "๐ŸŒฎ", "๐Ÿฃ"],
nature: ["๐ŸŒณ", "๐Ÿ‚", "๐ŸŒท", "๐ŸŒบ", "๐ŸŒธ"]
};
let animals, food, nature;
({animals, food, nature} = emojis);
console.log(animals); // ["๐Ÿถ", "๐Ÿฑ", "๐Ÿญ", "๐Ÿน", "๐Ÿฐ"]
console.log(food); // ["๐Ÿ•", "๐Ÿ”", "๐ŸŸ", "๐ŸŒฎ", "๐Ÿฃ"]
console.log(nature); // ["๐ŸŒณ", "๐Ÿ‚", "๐ŸŒท", "๐ŸŒบ", "๐ŸŒธ"]

Object destructuring offers you syntactical sugar to save extra lines of code.

const { animals, mythical } = creatures;
const animals = creatures.animals;
const mythical = creatures.mythical;

Object destructuring is a commonly used technique in React, particularly when working with function parameters.

const userData = {username: "Zipy", interests: ["Debugging", "Efficiency","Easy"] };
function printUserData({username, interests}) {
console.log(username, interests);
}
printUserData(userData);
const printUsername = ({username}) => console.log(username);
printUsername(userData);

For cleaner code, React developers use this pattern with props, which are the input for React components.

function MyReactComponent({name, age}) {
  // ...
}

Assigning in combination with renaming variables might be useful to increase the readability of your code.

const entities = {
animals: ["๐Ÿถ", "๐Ÿฑ", "๐Ÿป"],
humans: ["๐Ÿ‘จ๐Ÿฟ‍๐Ÿ’ผ", "๐Ÿ‘ฉ๐Ÿผ‍๐Ÿ’ผ", "๐Ÿง‘๐Ÿป‍๐Ÿ’ผ"]
};
const { humans: people } = entities;
console.log(people); // ["๐Ÿ‘จ๐Ÿฟ‍๐Ÿ’ผ", "๐Ÿ‘ฉ๐Ÿผ‍๐Ÿ’ผ", "๐Ÿง‘๐Ÿป‍๐Ÿ’ผ"]

You can also define default values while unpacking fields from the assigned object.

Take Javascript debugging to the next level, with Zipy!

Get Started for free

Rest and spread operators in JavaScript

The spread and rest operators in JavaScript are represented by three dots (...) which are some key concepts to learn React. The rest operator is used to gather or collect user-supplied values and store them in a JavaScript array or object, representing the 'rest' of those values.

Here's an example of using the rest operator to gather function arguments into an array:

<pre class="codebox">
<code id="myCode" class="codeblock">
</code>
</pre>

In the above code, the ...numbers syntax is the rest operator that collects all the arguments passed to the calculateSum function into an array called numbers. The function then iterates through this array and calculates the sum of all the numbers. By using the rest operator, we can pass any number of arguments to the function, and they will all be collected into the numbers array.

Spread operator

The spread operator is a feature in JavaScript that enables us to expand array items. It allows us to retrieve a list of parameters from an array. Unlike the rest operator, the spread operator operates in the opposite direction. It has a similar syntax to the rest operator but with different functionality.

It's important to note that the spread operator is only effective within array literals, function calls, or initialized property objects.

For example, in React, the spread operator is commonly used for passing props to child components. It enables the parent component to pass down a set of props to its child components without having to manually specify each prop. This can significantly reduce the amount of code needed and make the component hierarchy more modular and reusable.

Here is an example of how the spread operator can be used in JavaScript:

const numbers = [1, 2, 3, 4];
const maxNumber = Math.max(...numbers); // spread operator used to pass array items as arguments

console.log(maxNumber); // Output: 4

In the example above, the spread operator is used to pass the numbers array as arguments to the Math.max() method. Without the spread operator, the Math.max() method would not be able to accept the array as an argument.

The spread operator allows the array items to be spread out and passed as separate arguments to the method. In this case, the Math.max() method returns the maximum value in the array, which is 4.

reduce() in JavaScript

The reduce() function is considered the most powerful method for manipulating arrays and its a good tool to know before you learn React. It can replace the filter() and find() methods and is particularly useful for map() and filter() operations on large datasets.

Using map() and filter() in conjunction involves iterating over the array twice, first filtering each value and then mapping the remaining values. In contrast, the reduce() function allows for filtering and mapping in a single pass. Although more complex, the reduce() method is more efficient and powerful.

To use reduce(), we iterate over the array and pass in a callback function, similar to map(), filter(), and find(). However, the key difference is that reduce() reduces the array to a single value, which can be a number, array, or object.

It is important to note that reduce() takes two arguments, which is not the case with other array methods discussed in this tutorial.

Here's an example of using the reduce() method to calculate the sum of all the elements in an array:

const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, currentValue) => {
  return accumulator + currentValue;
}, 0);

console.log(sum); // Output: 15

In the example above, we start by defining an array of numbers. We then call the reduce() method on this array and pass in a callback function that will be executed for each element of the array.

The callback function takes two parameters - accumulator and currentValue. The accumulator parameter stores the accumulated result of the previous callback invocation, and the currentValue parameter stores the value of the current element being processed.

In this example, we initialize the accumulator value to 0. Then, for each element in the array, we add the currentValue to the accumulator. Finally, the reduce() method returns the accumulated result, which in this case is the sum of all the elements in the array.

Async/Await

Improve your understanding of React JS with Async/Await, a feature in JavaScript that enables the writing of asynchronous code in a synchronous manner. This eliminates the need for excessive nesting of callbacks.

When you define an async function, it always returns a promise. This allows you to use the await keyword to wait for the promise to be resolved before continuing with the execution of the next line of code.

In simple terms, synchronous execution means that tasks are completed in a sequential order, one after the other. On the other hand, asynchronous execution means that tasks are executed independently and may complete at different times.

It's worth noting that the async keyword must always precede the function declaration for you to use the await keyword inside the function.

Suppose you want to fetch data from an external API using fetch() and then display it on the webpage. Here's how you could do it using async/await:

async function getData() {
  try {
    const response = await fetch('https://jsonplaceholder.typicode.com/posts');
    const data = await response.json();
    // do something with the data, e.g. display it on the webpage
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

In this example, we define an async function called getData() that will fetch data from an API endpoint. Inside the function, we use the try/catch statement to handle any errors that may occur during the fetch operation.

We use the await keyword to wait for the promise returned by fetch() to resolve before continuing with the execution of the next line of code. Once the response is obtained, we use await again to parse the response using the json() method.

Finally, we log the parsed data to the console. You could also display the data on a webpage or perform other operations as required.

Overall, using async/await simplifies asynchronous programming in JavaScript by allowing you to write code in a more synchronous, readable and error-handling-friendly manner.

Take Javascript debugging to the next level, with Zipy!

Get Started for free

Conclusion- get started NOW!

JavaScript and React have many overlapping concepts and learning the points we have shared today is just an early steps in understanding React JS.

As you work with React, you'll naturally encounter new concepts and features in JavaScript that you can learn on the fly. The key is to maintain a balance between learning and application. Don't let the fear of not knowing everything about JavaScript stop you from starting with React.

Remember, React is a JavaScript library, so understanding the basics of the Javascript is crucial. However, you don't need to be an expert in JavaScript to learn React. With practice and experience, you'll gradually become more proficient in both JavaScript and React. So, don't hesitate to jump right in and start learning!

Call to Action

Feel free to comment or write to us in case you have any further questions at support@zipy.ai. We would be happy to help you. In case you want to explore for your app, you can sign up or book a demo.











Fix bugs faster with Zipy!

Get Started for Free
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Want to solve customer bugs even before they're reported?

The unified digital experience platform to drive growth with Product Analytics, Error Tracking, and Session Replay in one.

SOC 2 Type 2
Zipy is GDPR and SOC2 Type II Compliant
ยฉ 2023 Zipy Inc. | All rights reserved
with
by folks just like you
// open links in new tab script