React is build on top of pure Javascript for the most part. Here you will learn about 10 Javascript concepts that will help you write better React code.
let is used for variables that need to be reassigned after creation.
Const is used for variables that don't need to be reassigned or changed.
Both of these variables are limited within the scope of the curly braces that surround them. (They are block scoped)
What happens when you try to reassign a const?
const newUser = true;
newUser = false; // TypeError: Assignment to constant variable.
let and const are the preferred way for declaring variables, as their behavior is predictable.
const is also recommended when declaring components in combination with arrow functions, since the components remain unchanged.
const Header = () => {
// Local variable declared with const
const username = "Bob";
return <Header>Welcome back, {username}!;
}
In Javascript
Template strings are much more dynamic than the basic String type in Javascript, obtained using single or double quotes. Using them is much easier to interpolate and paste values into strings.
It is enough to use the syntax ${} to insert a valid expression in Javascript. Concatenating and combining strings does not require the “+” operator, it is easier to write multi-line objects of type String, since you do not need to create new strings using the newline character (\ n) or carriage return (\ r).
In addition, enclosed quotes (single and double) can be used in pattern strings without fear of errors.
const username = "Fred";
// Code in which strings are concatenated using "+" is difficult to read
const greeting = "Hi " + username + ", how are you?";
// Template strings (``) are much easier to write and read
const username = "Anna";
// Dynamic values are inserted using the expression $ {}
const greeting = `Hi ${username}, how are you?`;
In React Template strings are used to create complex objects of the String class, as well as dynamically calculate the styles of elements depending on conditions. You can insert variable values, operators, function calls, etc.
function UserCard({ id, name }) {
// check user id ...
const isOddUser = id % 2 !== 0;
// Odds get a dark background
return <div className={idOddUser ? 'dark-bg' : ''}>{name}</div> }
<UserCard id={1} name="Bob" /> // displays UserCard with a dark background
In javascript
Shorthand syntax can be used with arrow functions. This makes the whole code shorter. You can replace the return keyword (the return occurs by default if there are no curly braces) and the function body (curly braces) with a large arrow (=>).
It’s easier to work with objects and classes through the this keyword. You can also remove brackets around a single parameter.
// Standard function
function capitalize(word) {
return word.toUpperCase();
}
// Arrow function
const capitalize = (word) => {
return word.toUpperCase();
}
// Use all the features of the arrow function
const capitalize = word => word.toUpperCase();
In React
If you can create a standard function in JavaScript, you can create a arrow function. As a rule, the second option is preferable because the syntax is simpler.
Most often, arrow functions are used to create components, as well as for higher-order array methods such as .map () or .filter ().
const UserList = ({ users }) => {
return (
{users.map((user, index) => ( ))}
);
}
In Javascript
Like the for loop, the map, filter, and reduce array methods allow you to iterate over the elements of an array:
These methods are shorter and easier to read than the for loop. By combining these methods and arrow functions, you can further reduce code.
// Purpose: convert the users array to usernames array
const users = [
{ name: "Bob", id: 1 },
{ name: "Jane", id: 2 },
{ name: "Fred", id: 3 }
];
const usernames = [];
// For loop
for (let i = 0; i < users.length; i++) {
usernames[i] = users[i]
}
usernames; // ["Bob", "Jane", "Fred"]
// .map() - concise + readable
const usernames = users.map(user => user.username);
usernames; // ["Bob", "Jane", "Fred"]
In React
The methods .map (), .filter (), .reduce () can be used to convert data. They are usually used to dynamically display elements and components using .JSX, since these methods can be linked in chains for successive transformations.
function UserList() {
const users = [
{ name: "Bob", id: 1 },
{ name: "Jane", id: 2 },
{ name: "Fred", id: 3 }
];
// Drop the user with id = 2 and then distribute the entries and display the usernames
return (
<ul>
{users
.filter(user => user.id !== 2)<
.map(user => <li key={id}>{user.name}</li>)
}
</ul>
);
};
In Javascript
Destructive assignment allows you to unpack arrays or objects into variables. This is a convenient concept since you don’t need to refer to the whole object when we want to use it.
Destructuring allows us to extract the value we need from the object, creating an independent variable for this value. We can make the code cleaner without referring to the object when we need only one of its values.
const user = {
name: "Reed",
username: "ReedBarger",
email: "[email protected]",
details: {
title: "Programmer"
}
};
// Access to the object without destructuring
console.log(`${user.name}, ${user.email}`); // logs: Reed, [email protected]
// Restructure the object to reduce repetition
const { name, email } = user;
console.log(`${name}, ${email}`); // logs: Reed, [email protected]
// Restructuring an object with a nested object "details"
const { username, details: { title } } = user;
console.log(`${username}, ${title}`); // logs: ReedBarger, Programmer
In React
Most often, a destructive assignment is used to obtain the value of a single property of an object. If we pass only one property of an object to a given component, we do not need all the others. Instead of referencing properties, we can destructure them and pass one variable to the component.
function App() {
return (
<div>
<h1>All Users</h1>
<UserList users={["Bob", "Jane", "Fred"]} />
</div>
);
}
function UserList({ users }) {
return (
<ul>
{users.map((user, index) => (
<li key={index}>{user}</li>
))}
</ul>
);
}
In javascript
The default parameter is useful to handle the event passed by the function without arguments. They will also help to avoid errors and make the code more predictable.
// No default parameters
function sayHi(name) {
return "Hi" + name;
}
sayHi(); // "Hi undefined"
// With default parameters
function sayHi(name = 'Bob') {
return "Hi" + name;
}
sayHi(); // "Hi Bob"
// With default parameters and arrow function
const sayHi = (name = 'Jane') => "Hi" + name;
sayHi(); // "Hi Jane"
In React
Default settings are often used when defining properties. In the example below, we use destructive assignment to get the 'username' parameter from the properties of the object. And although the property is not passed, the default value is set to 'guest' and the component still works.
const Header = ({ username = "guest" }) => {
return <header>Welcome, {username}!</header>;
}
<Header /> // prints: Welcome, guest!
In Javascript
The spread syntax allows you to expand objects (their key-value pairs) and arrays to get new objects. This syntax only works when creating a new object or array.
The spread syntax is good for combining the properties of an old object in a new one. When an object or array expands to create a new object or array, a temporary copy appears.
// Combine empty default data with user data from the subscription form
// using the operator operator
const user = {
name: "",
email: "",
phoneNumber: "",
};
const newUser = {
name: "ReedBarger",
email: "[email protected]",
};
//The last object, when expanding, replaces the value of the same properties of the previous object with its own
//
const mergedUser = { ...user, ...newUser };
mergedUser; // { name: "ReedBarger", email: "[email protected]", phoneNumber: "" };
In React
The spread syntax is great for dynamically creating new objects and arrays; it is often used in React libraries (like Redux) for more predictable data changes.
In addition, in React, such a technique can be used to transfer object data as a set of properties without accessing them in turn: for this, we can expand the object into a component, since we will get an object of a set of properties.
function App() {
const name = {
first: "Reed",
last: "Barger"
};
return (
<div>
{/*
<UserGreeting
first={name.first}
last={name.last}
/>
*/}
<UserGreeting {...name} />
</div>
);
}
function User({ first, last }) {
return (
<p>
Hi, {first} {last}
</p>
);
}
In Javascript
JavaScript has an abbreviated form for writing conditional if-else statements - ternary operation. Unlike if-else, ternary operations are expressions. This gives you great flexibility, allowing you to use them just like any other expression (such as $ {} in the case of pattern strings).
Ternary operations are not always better than the if-else statement. For example, when processing multiple conditions, the former will be unreadable.
let age = 26;
let greeting;
// Without the if-else operator in such cases, you can do. Here we just
// assign the value to the variable depending on the condition
if (age > 18) {
greeting = "Hello, fellow adult";
} else {
greeting = "Hey kiddo";
}
// Ternary operations do the same thing, but much shorter
const greeting = age > 18 ? "Hello, fellow adult" : "Hey kiddo";
greeting; // 'Hello, fellow adult';
In React
You can output through JSX one result if the condition is true, and another if it is false, while writing the code is much shorter than through if-else.
If the result needs to be output only if the condition is true, the && operator can be used.
const Navbar = () => {
const isAuth = true;
return (
<div>
// For authorized users, shows a list of links, for others - an authorization screen
{isAuth ? <AuthLinks /> : <Login />}
// Show profile to authorized users only
{isAuth && <UserProfile/>}
</div>
);
}
In Javascript
Using ES modules, it is convenient to distribute code among application files. We export what we want to transfer to other files of our application (mainly variables and functions), and then import them where necessary.
You can perform multiple export / import using curly brackets (and keywords export / import) or single without brackets (with keywords export default and import).
This approach allows you to make the code modular. We can write code where it is needed without collecting everything into one large file. The example below shows how to call the getLocalTime function from a separate file in app.js.
// utils/getLocalTime.js
const getLocalTime = () => new Date().toLocaleTimeString();
export default getLocalTime;
// app.js
import getLocalTime from './utils/getLocalTime.js'
const App = () => {
return (
<div>
<header>The time is {getLocalTime()}</header>
...
</div>
);
}
In React
You can export and import almost anything, not just JS, but also CSS, image files, etc.
In React, you often do not need to add a file extension when importing JavaScript. Extensions are needed only when importing other file types into JS.
// App.js
const App = () =>
hello world!
// styles.css
html, body {
margin: 0;
padding: 0;
}
h1 {
color: cornflowerblue;
}
// index.js
import React from 'react';
import './styles.css'
import ReactDOM from "react-dom";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
In Javascript
You can delay indefinitely the execution of certain sections of code in JavaScript (for example, setTimeout (), the listener event, or the network request with the fetch API).
Promises are a way to make asynchronous JS code predictable. They help resolve code created with async. Successfully executed code is processed using the .then () callback functions, errors are processed using the .catch () function;
async / await is an improved syntax for working with promises that makes asynchronous code look synchronous.
// Asynchronous code; 'done' is logged after position data, although 'done' is assumed
// execute in code later
navigator.geolocation.getCurrentPosition(position => {
console.log(position);
}, error => {
console.error(error);
});
console.log("done");
// Asynchronous code processed after the promise; we get the desired result - position data
// logged, then logged 'done'
const promise = new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject);
});
promise
.then(position => console.log(position))
.catch(error => console.error(error))
.finally(() => console.log('done'));
// Asynchronous code with async / await looks like a synchronous, most readable way
// work with promises
async function getPosition() {
// async / await only works in functions (for now)
const result = await new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject);
});
const position = await result;
console.log(position);
console.log('done');
}
getPosition();
In React
Promises and async / await are used to create network requests such as API calls.
Libraries like the fetch API or axios apply promises to requests that can take indefinitely to complete. Promises and async / await in similar libraries are also used to form network requests:
// Get data through the API using the basic syntax of the promise (pay attention to arrow functions)
window.fetch('http://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(data => console.log(data));
// Get the same data through the API using async / await
async function getPostData() {
const response = await window.fetch('http://jsonplaceholder.typicode.com/posts')
// We need to allow two promises using await to get the resulting data
const data = await response.json();
console.log(data);
}
getPostData();
Practice makes perfect. After a long time of practicing, our work will become natural, skillfull, swift, and steady.
My main intention with this article is to help you write better and more efficient code. If you have any questions please contact me, I will be glad to answer all of your questions.
All the best -Dannison Arias