JavaScript concepts to know before learning React

Aryan Raj
10 min read | Published on : May 30, 2024
Last Updated on : Jul 30, 2024





Table of Contents

Are you eager to begin with React but unsure of where to begin? Prior to diving into the React ecosystem and determining how to launch your React application, it is essential to have a firm grasp of basic JavaScript fundamentals. Make sure you have a solid understanding of JavaScript variables, data types, conditional statements, loops, arrays, objects, and functions by avoiding the common mistake of adopting a "learn as you go" approach.

Learn about the newest ES6 syntax, including spread operators, destructuring, and arrow functions. You should also be conversant with asynchronous programming concepts, such as async/await and promises. We will also look at how you can utilise your knowledge of the Document Object Model (DOM) and JavaScript to manipulate it to your advantage when creating React-based user experiences.

Let's explore these JavaScript ideas and provide you with the knowledge you need to master React!

Take Javascript debugging to the next level, with Zipy!

Get Started for free

Callback functions in JavaScript

A callback function is one that runs subsequent to another function's conclusion. Usually, it is given to another function as an argument.

Understanding callbacks is crucial because they are utilised in many different contexts, including setTimeout(), event handlers like click and scroll, and array methods like map(), filter(), and so on.

An arrow function or a regular function can be used as callbacks. In JavaScript, the arrow function provides a succinct method for writing anonymous functions.

Take the following example of an event listener for the 'click' event, for example, which will be triggered each time the button is pressed and has a callback function:

//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

Once the main function has finished running, a callback function is triggered. Chaining callback functions together allows you to choose when a certain function runs, either after the parent function completes its execution or after a predetermined amount of time.

Using nested setTimeout calls, the following code demonstrates how to log five names in the console with a 5-second interval between each name. Five seconds after the function is invoked, the first name is logged. Subsequent names are logged one after the other, five seconds apart.

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 aforementioned example will work as planned, but because of the intricate layered callbacks, it can be difficult to comprehend, debug, or add error handling. This state of affairs is known as "Callback Hell." One major issue that arises from writing with deeply nested callbacks is called Callback Hell.

Promises help people get out of Callback Hell's sticky situation. They make it possible to write asynchronous code in a more direct, synchronous fashion.

An object that symbolises a value that is not yet available but is anticipated to become available in the future is called a promise.

Promises are frequently used in HTTP requests, which are asynchronous processes that require sending a request and then waiting for a response. Only once the server has responded can one retrieve the outcome (data or an error).

This is an illustration of how promises are written 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

JavaScript has a feature called the spread operator which allows us to expand array items. It enables us to take an array and extract a list of parameters. The spread operator functions in the opposite way as the rest operator. Although it provides distinct capabilities, it shares a syntax with the rest operator.

The spread operator only works in array literals, function calls, and initialised property objects, so keep that in mind.

For instance, the spread operator in React is frequently used to pass props to child components. Without having to individually declare each prop, it allows the parent component to send down a set of props to its child components. This can greatly minimise the amount of code required and increase the modularity and reusability of the component hierarchy.

This is an illustration of how to use the spread operator 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

Before learning React, it's a good idea to familiarise yourself with the reduce() function, which is regarded as the most potent way to manipulate arrays. It is especially helpful for map() and filter() operations on huge datasets and can replace the filter() and find() functions.

When map() and filter() are used together, the array is iterated over twice: each time, each value is filtered, and the remaining values are mapped. The reduce() function, on the other hand, enables filtering and mapping in a single pass. The reduce() approach is more potent and efficient, despite being more complicated.

Like with map(), filter(), and find(), we traverse over the array and pass in a callback function to use reduce(). The main distinction is that reduce() condenses the array into a single value, which may be an object, array, or number.

Unlike other array methods covered in this article, reduce() requires two arguments, which is a crucial distinction to make.

Here's an illustration of how to compute the sum of all the members in an array using the reduce() method:

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!

Wanna try Zipy?

Zipy provides you with full customer visibility without multiple back and forths between Customers, Customer Support and your Engineering teams.

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

product hunt logo
G2 logoGDPR certificationSOC 2 Type 2
Zipy is GDPR and SOC2 Type II Compliant
ยฉ 2024 Zipy Inc. | All rights reserved
with
by folks just like you