28
What JavaScript do you need to know to write Single-Page-Applications more effectively: A Guide
If you are interested in knowing what aspects of JavaScript are going to be important for frameworks such as React or Vue, then this article is for you.
I will be covering what aspects of JavaScript I have used when developing using React or Vue.
- JavaScript Basics
- Basic DOM Manipulation
- Arrow Functions
- Array Methods
- Asynchronous JavaScript
- Making Requests to an API using Fetch
- ES Modules
- NPM
Before we jump-in, I would just like to mention that it is not absolutely necessary to know all of these concepts to use React or Vue.
I definitely did not know everything when I first started using React and Vue.
Understanding these concepts and how JavaScript works in the browser is important and will give you a good foundation for your learning of React and Vue.
As with everything, the basics of JavaScript are going to be important.
In JavaScript you can create variables using the keyword const, var and let.
const is used to declare constants - these are variables whose value will not change over time.
const daysOfTheWeek = 7;
let and var are used in the same way with the keyword being placed before the variable name:
let firstName = "Jane";
var lastName = "Doe";
These are generally used when we have variables whose values are going to change over time.
You may be asking yourself, why there are two keywords with the same function.
There is a small difference.
function varExample ()
{
var name = "Jane";
if(name === "Jane")
{
var name = "Doe"; // the same variable as above
console.log(name) // will return "Doe"
}
console.log(name); // will also return "Doe"
}
function letExample ()
{
let name = "Jane";
if(name === "Jane")
{
let name = "Doe"; // new variable is created
console.log(name) // will return "Doe"
}
console.log(name); // will also return "Jane"
}
The variable created using var gets updated, regardless of where in the function it is called.
The variable created using let does not get updated, because the value of that variable is specific to the block it is created in.
Now that we know how to create variables in JavaScript, we can move on to looking at datatypes in JavaScript.
When writing JavaScript, you will not be declaring the type of variable you are creating.
However, it can be helpful to know which data types exist in the wild west of JavaScript:
- Undefined
- Number
- Boolean
- String
- BigInt
- Symbol
For more information on types in JavaScript you can have a look at the awesome documentation by Mozilla
Being able to manipulate strings is another task that is often needed when building an application.
let firstName = "Jane";
let lastName = "Doe";
//joining them using a '+'
let fullNameOne = firstName + " " + lastName;
console.log(fullNameOne);
//or using template string by using backticks(``)
let fullNameTwo = `${firstName} ${lastName}`;
console.log(fullNameTwo);
//both will result in "Jane Doe"
We can directly use the variables we created to build our final string that will be used as part of our user interface.
Strings also have properties we can access such as the length:
let firstName = "Jane"
let firstNameLength = firstName.length
//will output "4"
console.log(firstNameLength);
We can also convert strings to upper- and lower-case using built-in methods:
let firstName = "Jane";
let upperCaseName = firstName.toUpperCase();
let lowerCaseName = firstName.toLowerCase();
console.log(upperCaseName);//Output: "JANE"
console.log(lowerCaseName);//Output: "jane"
These are again just a small sub-section of what you can do using the built-in JavaScript methods.
JavaScript is definitely a powerful language for the web. It allows us to directly alter what we see in the browser. Using JavaScript we can respond to user input.
It is this reactivity that is an important concept to understand, especially when we progress to using React or Vue.
The first step in using JavaScript to manipulate the DOM is to know how to select the elements you want to manipulate.
The older approach you may see is this one:
//this will store all elements that have a class of "title" within our heading variable
let heading = document.getElementByClassName("title");
//this will store the element with the Id "submit" within our button variable
let button = document.getElementById("submit");
A newer approach is to use querySelector():
//this will store the *first* element that has a class of "title" within our heading variable
let heading = document.querySelector(".title");
//this will store the element with the Id "submit" within our button variable
let button = document.querySelector("#submit");
//this will store all of the elements that have a class of "title" within our heading variable as an array
let heading = document.querySelectorAll(".title");
Another great feature of JavaScript is the ability to create elements that get rendered in the browser.
Helpful when you are retrieving data from an api and you want to display it in a list.
//here we have our heading
const heading = document.querySelector('.heading');
//now let us add our subheading
const subheading = document.createElement('h5');
subheading.textContent = "I am a subheading";
//we can now add this subheading to the heading element
heading.appendChild(subheading);
This may well be one of the most common tasks when it comes to writing React and Vue apps. Which is why it is also important to understand how it works.
Assume we have a form and we want to get the input the user has entered into the field for the username.
There will be two parts to this form, an input field and a button:
<input type="text" class="username">
<button class="submit-btn" onClick="submitData()">
Submit
</button>
Using onClick we can define what needs to happen when the button is clicked:
const submitData = () => {
let username = document.querySelector(".username").value;
console.log(username); //will print our the text entered in the input field to the console
}
We can also adjust this to validate the user input every time a new character is added to the input:
<input type="text" class="username" onkeyup="logInput()">
<button class="submit-btn" onClick="submitData()">
Submit
</button>
const logInput = () => {
let username = document.querySelector(".username").value;
console.log(username);
//will produce if typed slow enough:
// "J"
// "Ja"
// "Jan"
// "Jane"
}
There are many different events that can be triggered based on user input.
JavaScript allows us to dynamically update the styling of our UI, allowing us to give feedback to user in response to their input.
Very handy when we are validating the e-mail in a form:
<input type="text" class="user-email" onkeyup="validateInput()">
We are validating the e-mail on every character, giving the user the feedback and letting the know, if their input is valid or not.
This saves them the frustration of submitting data that they then have to correct later on, after the submit fails the validation check.
const validateInput = () => {
//select the input element
let emailInputElement = document.querySelector(".user-email");
//get the value of the input field
let userEmail = emailInputElement.value;
//decide if the e-mail is valid or not
if(!userEmail.includes("@"))
{
//here we are adding the red border of the e-mail is valid
emailInputElement.classList.add("invalid-input");
//and removing it, if the e-mail becomes invalid again
emailInputElement.classList.remove("valid-input");
} else {
//here we add the green border if it is valid
emailInputElement.classList.add("valid-input");
//and remove the red border
emailInputElement.classList.remove("invalid-input");
}
}
With these manipulations I definitely would encourage you to try and build a few small projects where you focus on using JavaScript to build-in some reactivity into your app.
In a couple of the examples I have shown you, you may already seen this "=>" appear. These are called arrow functions and allow us to simplify the more traditional function declaration:
//traditional JavaScript function
function generateFullName(firstName, lastName){
return `${firstName} ${lastName}`;
}
//will return "Jane Doe"
console.log(generateFullName("Jane", "Doe"));
//arrow function with name
const generateFullNameArrow = (firstName, lastName) => `${firstName} ${lastName}`
//arrow function returning "Jane Doe"
console.log(generateFullNameArrow("Jane", "Doe"));
These are perhaps one of the most widely used aspects of JavaScript when it comes to dealing with data being retrieved from an API.
In JavaScript arrays can be created using the following syntax:
let nameArray = ["Jane", "John", "Sarah", "Mike"];
Very simple right?
We store the data in a variable, which is why we need a let at the beginning.
Arrays have different methods that allow us to interact and manipulate the data inside.
To go over each item in array we can use the forEach method:
nameArray.forEach(name => console.log(name));
// Output: "Jane"
// Output: "John"
// Output: "Sarah"
// Output: "Mike"
This does exactly the same as a for-loop:
for(let i = 0; i < nameArray.length; i++)
{
console.log(nameArray[i]);
}
The forEach method allows us to write less code, but there is not a right or wrong way to go about this.
The console.log(nameArray[i]);
is how we can access specific elements within an array.
We need to know what index the element within an array has.
For our nameArray array, we have the following:
//index 0 1 2 3
let nameArray = ["Jane", "John", "Sarah", "Mike"];
//accessing the name Sarah
console.log(nameArray[2]);
In JavaScript the index starts at 0 and goes up.
JavaScript also has a built-in filter() function that allows us to take the original array and create a new one with items, that fulfill a certain criteria.
//will give us a new array with names that have 4 letters or less
let namesThatHaveFourLetters = nameArray.filter(name => name.length <= 4);
//output: ["Jane", "John", "Mike"]
console.log(namesThatHaveFourLetters);
This will only include names that have 4 characters or less in them.
Another great method that I can recommend using is the map() method.
It allows us to apply changes to each item of an array:
let randomNumbersArray = [1, 2, 3, 4, 5];
let doubledNumbersArray = randomNumbersArray.map(number => number * 2);
console.log(doubledNumbersArray);
//output: [2, 4, 6, 8, 10]
Another task that does come up is adding and removing items from an array:
//add item to the end of an array
let nameArray.push("Amy");
//add item to the start of an array
let nameArray.unshift("Tom");
//remove item from the end of an array
let nameArray.pop(); //removes "Amy" from array
//remove item from the start of an array
let nameArray.shift(); // removes "Tom" from array
I again recommend that you have a look at the documentation for Mozilla, where there is a more complete list of what is possible with arrays in JavaScript.
This is a key concept when looking using JavaScript-based technologies.
A synchronous app would be one that we are generally familiar with - one line of code is executed after another and there are no two tasks being executed alongside each other.
This can become a problem when you are executing an intensive piece of code and are waiting for that to finish before going on to the next task. If this happens with in the browser, you may well see no response and think the browser has frozen.
You will find that many tasks that relate to fetching some sort of resource from a server now feature asynchronous code to run.
Using setTimeout we can easily show how JavaScript enables us to run code asynchronously:
setTimeout( () => {
console.log("First console log");
}, 2000);
console.log("Second console log");
//Output:
//"Second console log"
//"First console log"
This is using callbacks to run whatever is inside of the function only after 2 seconds have elapsed.
This means that JavaScript goes onto the next line while the function is waiting to be resolved.
There is a newer and more modern way of writing these types of asynchronous tasks using Promises:
fetch("api/for/some/resource")
//Promises have a characteristic .then()
.then( response => {
console.log(response.data);
//it is common to use .catch() to log any errors
}).then( json => {
console.log(json);
}).catch( error => {
console.log(error);
});
The code contained within .then() will only be executed, when fetch() returns a result. If the it returns an error instead, the .catch()-block will be invoked.
There is a third level of asynchronous JavaScript that can be achieved using async/await:
//the async keyword comes before the function you want to use await in
const data = async () => {
//get the resource returned by the api
const resource = await fetch("api/for/some/resource")
//convert the returned data to json
const posts = await resource.json();
//make it available
return posts;
}
As you can see this does make your code more readable and cleaner to understand.
When working on the frontend of a project, one of the main tasks will be to send and receive data from the backend.
The fetch API provides us with a very convenient way of doing exactly this:
const getUserData = async () => {
const response = await fetch('api/user/resource', {
method: 'GET' //'POST', 'PUT', 'PATCH', 'DELETE'
});
const data = await response.json();
return data;
}
If we wanted to post data to the server, we simply use the 'POST' method:
const formData = { firstName: "Jane" };
const postUserData = async () => {
const response = await fetch('api/user/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8'
},
body: JSON.stringify(formData),
});
const data = await response.json();
return data;
}
The same thing can be done will all other Http verbs that you can use.
This forms pretty much the backbone of building singe-page-applications using Vue or React.
Modules allow us to write logic in one file, export the logic we want and import it in the file we need.
This was possible before we had modules:
const library = require('library-name);
While the module syntax looks like:
import library from 'library-name';
In order for us to be able to import any library or logic, we need to first make it available to be imported. This is done using the export keyword.
//roundNumber.js
export default decimal => Math.round(decimal);
We can now use this in our app.js file:
//app.js
import roundNumber from './roundNumber.js';
let decimal = 3,2;
let roundedDecimal = roundNumber(decimal);
In a Vue or React project you will definitely be importing functionality from different third-party tools and also exporting your own logic to be used within the entire app.
When you first start out a new job as a developer, chances are you will be working on an existing project.
This means that you will need to setup and install all of the dependencies for the project.
Luckily, we have something called npm with which we can easily install all of the dependencies defined within the project's package.json file.
To install all of the packages defined in the file, simply run npm install
.
You will see a node_modules-directory created, where, you guessed it, all of your modules/dependencies are installed, ready to be imported.
You can also add packages to a project using npm install <package-name>
.
Additionally, we can define which modules are going to be used in production (use npm install <package-name> --save
) and which are pure development dependencies (use npm install <package-name> --save-dev
).
Testing libraries are usually used during development, but are not needed during production.
Lastly, we can also define commands within our package.json that we can then run using npm:
//package.json
{
"scripts": {
"dev": "vue-cli-service serve",
}
}
We can then reference this using npm by running npm run dev
which will in this case start up the development server of our Vue app.
I hope this brief overview gave you an idea of what to know when it comes to starting with React or Vue.
Like I wrote in the beginning, it is not necessary to know all of this, but it does help and speed-up your progress by being familiar with these concepts.
Let me know in the comments if I missed anything that is relevant to learning.
28