Introduction to React
React is a declarative, efficient, and flexible JavaScript library for building user interfaces. It lets you compose complex UIs from small and isolated pieces of code called “components”.
Single-page Application
A single-page application is an application that loads a single HTML page and all the necessary assets (such as JavaScript and CSS) required for the application to run. Any interactions with the page or subsequent pages do not require a round trip to the server which means the page is not reloaded.
Though you may build a single-page application in React, it is not a requirement. React can also be used for enhancing small parts of existing websites with additional interactivity. Code written in React can coexist peacefully with markup rendered on the server by something like PHP, or with other client-side libraries. In fact, this is exactly how React is being used at Facebook.
Create React App
is a comfortable environment for learning React, and is the best way to start building a new application in React. It sets up your development environment so that you can use the latest JavaScript features, provides a nice developer experience, and optimizes your app for production. You’ll need to have on your machine. To create a project, run: npx create-react-app my-app --template typescript
cd my-app
npm start
Under the hood, it uses and , but you don’t need to know anything about them. Bundler, such as or . It lets you write modular code and bundle it together into small packages to optimize load time. Compiler such as . It lets you write modern JavaScript code that still works in older browsers. When you’re ready to deploy to production, running npm run build will create an optimized build of your app in the build folder.
Project structure
Running create-react-app command will create a directory called my-app inside the current folder.
Inside that directory, it will generate the initial project structure and install the transitive dependencies:
my-app
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── serviceWorker.js
└── setupTests.js
We will update src folder in following way (and there are a lot more ways to organize this folder, this is just our current standard way of doing it)
└── src
└── assets
└── images
└── svg
└── components
└── elements
└── modules
└── templates
└── constants
└── helper
└── lib
└── pages
├── index.jsx
└── styles
├── main.css
├── App.js
├── index.js
Working with Virtual DOM
DOM: DOM stands for ‘Document Object Model’. In simple terms, it is a structured representation of the HTML elements that are present in a webpage or web-app. DOM represents the entire UI of your application. The DOM is represented as a tree data structure. It contains a node for each UI element present in the web document. It is very useful as it allows web developers to modify content through JavaScript, also it being in structured format helps a lot as we can choose specific targets and all the code becomes much easier to work with. If you want to learn more about DOM, visit this Updating DOM: If you know a little about JavaScript then you might have seen people making use of ‘getElementById()’ or ‘getElementByClass()’ method to modify the content of DOM. Every time there is a change in the state of your application, the DOM get’s updated to reflect that change in the UI. Though doing things like this is not a problem and it works fine, but consider a case where we have a DOM that contains nodes in a large number, and also all these web elements have different styling and attributes. As DOM is represented as a tree itself, updating the tree her e is not a costly operation indeed we have a lot of algorithms on trees to make the updates fast. What’s proving to be costly is every time the DOM gets updated, the updated element and its children have to be rendered again to update the UI of our page. Like this each time there is a component update, the DOM needs to be updated and the UI components have to be re-rendered.
Example:
// Simple getElementById() method
document.getElementById('some-id').innerValue = 'updated value';
When writing the above code in the console or in the JavaScript file, these things happen:
The browser parses the HTML to find the node with this id. It removes the child element of this specific element. Updates the element(DOM) with the ‘updated value’. Recalculates the CSS for the parent and child nodes. Finally, traverse the tree and paint it on the screen(browser) display. So as we know now that updating the DOM not only involves changing the content, it has a lot more attached to it. Also recalculating the CSS and changing the layouts involves complex algorithms, and they do affect the performance. So React has a different approach to dealing with this, as it makes use of something known as Virtual DOM.
Virtual DOM: React uses Virtual DOM exists which is like a lightweight copy of the actual DOM(a virtual representation of the DOM). So for every object that exists in the original DOM, there is an object for that in React Virtual DOM. It is exactly the same, but it does not have the power to directly change the layout of the document. Manipulating DOM is slow, but manipulating Virtual DOM is fast as nothing gets drawn on the screen. So each time there is a change in the state of our application, virtual DOM gets updated first instead of the real DOM. You may still wonder, “Aren’t we doing the same thing again and doubling our work? How can this be faster?” Read below to understand how things will be faster using virtual DOM.
How Virtual DOM actually make the things faster: When anything new is added to the application, a virtual DOM is created and it is represented as a tree. Each element in the application is a node in this tree. So, whenever there is a change in state of any element, a new Virtual DOM tree is created. This new Virtual DOM tree is then compared with the previous Virtual DOM tree and make a note of the changes. After this, it finds the best possible ways to make these changes to the real DOM. Now only the updated elements will get rendered on the page again.
How Virtual DOM helps React: In react, everything is treated as a component be it a functional component or class component. A component can contain a state. Each time we change something in our JSX file or let’s put it in simple terms, whenever the state of any component is changed react updates it’s Virtual DOM tree. Though it may sound that it is ineffective but the cost is not much significant as updating the virtual DOM doesn’t take much time. React maintains two Virtual DOM at each time, one contains the updated Virtual DOM and one which is just the pre-update version of this updated Virtual DOM. Now it compares the pre-update version with the updated Virtual DOM and figures out what exactly has changed in the DOM like which components have been changed. This process of comparing the current Virtual DOM tree with the previous one is known as ‘diffing’. Once React finds out what exactly has changed then it updated those objects only, on real DOM. React uses something called as batch updates to update the real DOM. It just mean that the changes to the real DOM are sent in batches instead of sending any update for a single change in the state of a component. We have seen that the re-rendering of the UI is the most expensive part and React manages to do this most efficiently by ensuring that the Real DOM receives batch updates to re-render the UI. This entire process of transforming changes to the real DOM is called Reconciliation
This significantly improves the performance and is the main reason why React and its Virtual DOM is much loved by developers all around.
JSX
Consider this variable declaration:
const element = <h1>Hello, world!</h1>;
This funny tag syntax is neither a string nor HTML.
It is called JSX, and it is a syntax extension to JavaScript. It is used with React to describe what the UI should look like and it comes with the full power of JavaScript.
JSX produces React “elements”.
Why JSX?
React embraces the fact that rendering logic is inherently coupled with other UI logic: how events are handled, how the state changes over time, and how the data is prepared for display.
Instead of artificially separating technologies by putting markup and logic in separate files, React with loosely coupled units called “components” that contain both. We will come back to components in a , but if you’re not yet comfortable putting markup in JS, might convince you otherwise. React using JSX, but most people find it helpful as a visual aid when working with UI inside the JavaScript code. It also allows React to show more useful error and warning messages. With that out of the way, let’s get started!
Embedding Expressions in JSX
In the example below, we declare a variable called name and then use it inside JSX by wrapping it in curly braces:
const name = 'Delila Devic';
const element = <h1>Hello, {name}</h1>;
ReactDOM.render(
element,
document.getElementById('root')
);
You can put any valid inside the curly braces in JSX. For example, 2 + 2, user.firstName, or formatName(user) are all valid JavaScript expressions. In the example below, we embed the result of calling a JavaScript function, formatName(user), into an <h1> element.
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Delila',
lastName: 'Devic'
};
const element = (
<h1>
Hello, {formatName(user)}! </h1>
);
ReactDOM.render(
element,
document.getElementById('root')
);
After compilation, JSX expressions become regular JavaScript function calls and evaluate to JavaScript objects.
This means that you can use JSX inside of if statements and for loops, assign it to variables, accept it as arguments, and return it from functions:
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>; }
return <h1>Hello, Stranger.</h1>;}
Specifying Attributes with JSX
You may use quotes to specify string literals as attributes:
const element = <img src=""></img>; You may also use curly braces to embed a JavaScript expression in an attribute:
const element = <img src={user.avatarUrl} marginLeft="20px"></img>;
Don’t put quotes around curly braces when embedding a JavaScript expression in an attribute. You should either use quotes (for string values) or curly braces (for expressions), but not both in the same attribute.
Warning:
Since JSX is closer to JavaScript than to HTML, React DOM uses camelCase property naming convention instead of HTML attribute names.
For example, class becomes in JSX, and margin-left becomes marginLeft.
Specifying Children with JSX
If a tag is empty, you may close it immediately with />, like XML:
const element = <img src={user.avatarUrl} />;
JSX tags may contain children:
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
JSX Represents Objects
Babel compiles JSX down to React.createElement() calls.
These two examples are identical:
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
React.createElement() performs a few checks to help you write bug-free code but essentially it creates an object like this:
// Note: this structure is simplified
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world!'
}
};
These objects are called “React elements”. You can think of them as descriptions of what you want to see on the screen. React reads these objects and uses them to construct the DOM and keep it up to date.
React Components
Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. One of major tasks while building React app is separating design in small, robust and reusable components.
Example:
Task:
Separate this page in components using Function and Class Components
The simplest way to define a component is to write a JavaScript function:
function Welcome(props) {
CONST LALALAL = 'DESELL;'
return <h1>Hello, {props.name}</h1>;
}
This function is a valid React component because it accepts a single “props” (which stands for properties) object argument with data and returns a React element. We call such components “function components” because they are literally JavaScript functions.
You can also use an to define a component: class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
The above two components are equivalent from React’s point of view.
Class component lifecycle
Each component has several “lifecycle methods” that you can override to run code at particular times in the process. In the list below, commonly used lifecycle methods are marked as bold. The rest of them exist for relatively rare use cases.
Mounting
These methods are called in the following order when an instance of a component is being created and inserted into the DOM:
Updating
An update can be caused by changes to props or state. These methods are called in the following order when a component is being re-rendered:
Unmounting
This method is called when a component is being removed from the DOM:
Error Handling
These methods are called when there is an error during rendering, in a lifecycle method, or in the constructor of any child component.
You can read more about them in this documentation Rendering a Component
Previously, we only encountered React elements that represent DOM tags:
However, elements can also represent user-defined components:
const element = <Welcome />;
For example, this code renders “Hello world” on the page:
function Welcome() {
return <h1>Hello user</h1>;
}
const element = <Welcome />;
ReactDOM.render(
element,
document.getElementById('root')
);
Note: Always start component names with a capital letter.
React treats components starting with lowercase letters as DOM tags. For example, <div /> represents an HTML div tag, but <Welcome /> represents a component and requires Welcome to be in scope.
Composing Components
Components can refer to other components in their output. This lets us use the same component abstraction for any level of detail. A button, a form, a dialog, a screen: in React apps, all those are commonly expressed as components.
For example, we can create an App component that renders Welcome many times:
function Welcome() {
return <h1>Hello user</h1>;
}
function App() {
return (
<div>
<Welcome />
<Welcome />
<Welcome />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Typically, new React apps have a single App component at the very top. However, if you integrate React into an existing app, you might start bottom-up with a small component like Button and gradually work your way to the top of the view hierarchy.
Working with fragments
A common pattern in React is for a component to return multiple elements. Fragments let you group a list of children without adding extra nodes to the DOM.
render() {
return (
<React.Fragment> // or <>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment> // or </>
);
}
Using css/scss in components
Component props
React Props are like function arguments in JavaScript and attributes in HTML.
To send props into a component, use the same syntax as HTML attributes:
<Welcome name="Delila" />;
// We need to define interface for every component that is using props
// We are doing this in order to ensure that component is going to //have valid values sent to it
interface WelcomeProps {
name: string;
}
// Type of props is always Object
const Welcome = (props: WelcomeProps) => {
return <h1>Hello {props.name}</h1>;
}
// or
// This is called Object descructing in js
const Welcome = ({name}: WelcomeProps) => {
return <h1>Hello {name}</h1>;
}
const element = <Welcome name="Delila"/>;
ReactDOM.render(
element,
document.getElementById('root')
);
Why do we need props at all ??
Let’s assume we need to create CLIENT QUOTES section from this Figma file:
Task: We start with creating ClientQuoteCard component (element) using hardcoded values from first card and rendering this component inside App.tsx.
Code example:
import React from 'react';
import google from "../../../assets/google.png";
import amazon from "../../../assets/amazon.png";
import robinhood from "../../../assets/robinhood.png";
import styles from './ClientQuoteCard.module.css'
const ClientQuoteCard = () => {
const clientGoogle = {
comment: "\"Easy to work with\"",
text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto accusantium praesentium eius, ut atque fuga culpa, similique sequi cum eos quis dolorum.",
image: google,
imageName: "Google",
}
return (
<div className={styles.client_div}>
<p className={styles.small_text}>Testimonials</p>
<h2>{clientGoogle.comment}</h2>
<p className={styles.text}>{clientGoogle.text}</p>
<div className={styles.betterleap_client}>
<img src={clientGoogle.image}></img>
<div className={styles.betterleap_client_div}>
<p className={styles.small_text} id={styles.client_name}>{clientGoogle.imageName}</p>
</div>
</div>
</div>
);
}
export default ClientQuoteCard;
Question: How are we going to use this component to show different values?
Answer: Using props
First thing that we need to decide is what properties should this component accept. The easiest way to do this is to figure out which data is dynamic and which static. Let’s do this together
Question: What props should we pass to ClientQuoteCard?
Answer: title, description, logo, companyName
Question: Do we have any optional props?
Answer: nop
Question:
Code sample after updating props:
import React from 'react';
import google from "../../../assets/google.png";
import amazon from "../../../assets/amazon.png";
import robinhood from "../../../assets/robinhood.png";
import styles from './ClientQuoteCard.module.css'
interface ClientQuoteCardProps {
title: string;
description: string;
logo: string;
companyName: string;
}
const ClientQuoteCard = ({title, description, logo, companyName}: ClientQuoteCardProps) => {
<div className={styles.client_div}>
<p className={styles.small_text}>Testimonials</p>
<h2>{title}</h2>
<p className={styles.text}>{description}</p>
<div className={styles.betterleap_client}>
<img src={logo}></img>
<div className={styles.betterleap_client_div}>
<p className={styles.small_text} id={styles.client_name}>{clientGoogle.imageName}</p>
<p className={styles.small_text} id={styles.client_name}>{companyName}</p>
</div>
</div>
</div>
);
}
export default ClientQuoteCard;
Task: Show 3 different ClientQuoteCards in one row without using any additional variables (ClientQuote module)
Code sample:
import google from "../../../assets/google.png";
import amazon from "../../../assets/amazon.png";
import robinhood from "../../../assets/robinhood.png";
import styles from './ClientQuotes.module.css'
import ClientQuoteCard from '../../elements/ClientQuoteCard/ClientQuoteCard';
function ClientQuotes() {
const clientGoogle = {
comment: "\"Easy to work with\"",
text: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Architecto accusantium praesentium eius, ut atque fuga culpa, similique sequi cum eos quis dolorum.",
image: google,
imageName: "Google",
isClient: true,
}
const clientAmazon = {
comment: "\"Found great candidates\"",
text: "Working with Ricardo to staff my eng office has been an excellent experience. Of all the recruiters I’ve worked with, Ricardo has - sent over the most high quality candidates...",
image: amazon,
imageName: "Amazon",
isClient: true,
}