The advent of modern JS frameworks has made it much easier to build highly dynamic, interactive applications. A framework is a library that offers opinions about how software gets built. These opinions allow for predictability and homogeneity in an application; predictability allows the software to scale to an enormous size and still be maintainable; predictability and maintainability are essential for the health and longevity of software.

Facebook released React in 2013. Technically, React itself is not a framework; it’s a library for rendering UI components. React is used in combination with other libraries to make applications. React and React Native enable developers to make mobile applications; React and ReactDOM enable them to make web applications, etc. Because React and ReactDOM are so often used together, React is colloquially understood as a JavaScript framework.

Why frameworks exist?

Consider a common kind of application: A to-do list creator. This application should allow users to do things like render a list of tasks, add a new task, and delete a task; and it must do this while reliably tracking and updating the data underlying the application. In software development, this underlying data is known as state.

Each of our goals is theoretically simple in isolation. We can iterate over the data to render it; we can add to an object to make a new task; we can use an identifier to find, edit, or delete a task. When we remember that the application has to let the user do all of these things through the browser, some cracks start to show. 

The real problem is this: every time we change our application’s state, we need to update the UI to match.

Working directly with the DOM, requires understanding many things about how the DOM works: how to make elements; how to change their properties; how to put elements inside of each other; how to get them on the page. None of this code actually handles user interactions, or addresses adding or deleting a task. If we add those features, we have to remember to update our UI at the right time and in the right way.

JavaScript frameworks were created to make this kind of work a lot easier — they exist to provide a better developer experience. They don’t bring brand-new powers to JavaScript; they give you easier access to JavaScript’s powers so you can build for today’s web.

Others things frameworks give us:

Tooling: Because each of the frameworks have a large community, each framework’s ecosystem provides tooling that improves the developer experience.

Compartmentalization: Most major frameworks encourage developers to abstract the different parts of their user interfaces into components — maintainable, reusable chunks of code that can communicate with one another. All the code related to a given component can live in one file (or a couple of specific files) so that you as a developer know exactly where to go to make changes to that component.

Routing: The most essential feature of the web is that it allows users to navigate from one page to another – it is, after all, a network of interlinked documents. When you follow a link on this very website, your browser communicates with a server and fetches new content to display for you. As it does so, the URL in your address bar changes. You can save this new URL and come back to the page later on, or share it with others so they can easily find the same page. Your browser remembers your navigation history and allows you to navigate back and forth, too. This is called server-side routing. Modern web applications typically do not fetch and render new HTML files — they load a single HTML shell, and continually update the DOM inside it (referred to as single page apps, or SPAs) without navigating users to new addresses on the web. Each new pseudo-webpage is usually called a view, and by default, no routing is done. When an SPA is complex enough, and renders enough unique views, it’s important to bring routing functionality into your application. People are used to being able to link to specific pages in an application, travel forward and backward in their navigation history, etc., and their experience suffers when these standard web features are broken. When routing is handled by a client application in this fashion, it is aptly called client-side routing.

Note: DSLs are programming languages relevant in specific areas of software development. In the context of frameworks, DSLs are variations on JS or HTML that make it easier to develop with that framework. Crucially, none of the frameworks require a developer to use a specific DSL, but they have almost all been designed with a specific DSL in mind. Choosing not to employ a framework’s preferred DSL will mean you miss out on features that would otherwise improve your developer experience.

React:

React apps are made out of components. A component is a piece of the UI (user interface) that has its own logic and appearance. A component can be as small as a button, or as large as an entire page.

React components are JS functions that return markup:

function MyButton(){
	return (
		<button>I'm a button</button>
	)
}

Now that we have declared MyButton, you can nest it into another component:

export default function MyApp() {
	return (
		<div>
			<h1>Welcome to my app</h1>
			<MyButton />
		</div>
	);
}

Notice that <MyButton /> starts with a capital letter. That’s how you know it’s a React component. React component names must always start with a capital letter, while HTML tags must be lowercase.

The export default keywords specify the main component in the file. The markup syntax you’ve seen above is called JSX. It is optional, but most React projects use JSX for its convenience. JSX is stricter than HTML. You have to close tags like
. Your component cant return multiple JSX tags. You have to wrap them into a shared parent, like a < div >…</ div > or an empty <> … </ > wrapper.

Note: If you have a lot of HTML to port to JSX, you can use an online converter.

Adding styles:

In React, you can specify a CSS class with className. It works in the same way as the HTML class attribute. Then you can write the CSS rules for it in a separate CSS file:

<img className="avatar" />

.avatar {
	border-radius: 50%;
}

React does not prescribe how you add CSS files.

Displaying data:

JSX lets you put markup into JavaScript. Curly braces let you “escape back” into JavaScript so that you can embed some variable from your code and display it to the user. For example, this will display user.name:

return (
	<h1>
		{user.name}
	</h1>
)

return (
	<img
		className = "avatar"
		src = {user.imageUrl}
	/>
)

Conditional rendering:

In React, there is no special syntax for writing conditions. Instead you will use the same techniques as you use when writing regular JS code. For example, you can use an if statement to conditionally include JSX. If you prefer more compact code, you can use the conditional ? operator. Unlike if, it works inside JSX. When you don’t need the else branch, you can use a shorter logical && syntax:

let content;
if (isLoggedIn) {
	content = <AdminPanel />;
} else {
	content = <LoginForm />;
}

return (
	<div>
		{content}
	</div>
)

<div>
	{isLoggedIn ? (
		<AdminPanel />
	) : (
		<LoginForm />
	)}
</div>

<div>
	{isLoggedIn && <AdminPanel />}
</div>

Rendering lists:

You will rely on JS features like for loop and array map() function to render lists of components. Notice how li has a key attributes. For each item in a list, you should pass a string or a number that uniquely identifies that item among its siblings. Usually, a key should be coming from your data, such as a database ID. React uses your keys to know what happened if you later insert, delete or reorder the items.

const products = [  
	{ title: 'Cabbage', id: 1 },  
	{ title: 'Garlic', id: 2 },  
	{ title: 'Apple', id: 3 },  
];

const listItems = products.map(product =>  
	<li key={product.id}>  
		{product.title}  
	</li>  
);  

return ( 
	<ul>{listItems}</ul>  
);

Responding to events:

You can respond to events by declaring event handler functions inside your components. Notice how onClick={handleClick} has no parentheses at the end! Do not call the event handler function: you only need to pass it down. React will call your event handler when the user clicks the button.

function MyButton() {
	function handleClick() {
		alert('You clicked me!');
	}

	return (
		<button onClick={handleClick}>
			Click me
		</buttom>
	);
}

Updating the screen:

Often you will want your component to remember some information and display it. For example, maybe you want to count the number of times a button is clicked. To do this, add state to your component.

First, import useState from React:

import { useState } from 'react';

Now you can declare a state variable inside your component:

function MyButton() {
	const [count, setCount] = useState(0);
}

You will get two things from useState: the current state (count), and the function that lets you update it (setCount). You can give them any names, but the convention is to write Something, setSomething.

The first time the button is displayed, count will be 0 because you passed 0 to useState(). When you want to change state, call setCount() and pass the new value to it. Clicking this button will increment the counter:

function MyButton() {  
	const [count, setCount] = useState(0);  

	function handleClick() {  
		setCount(count + 1);  
	}  

	return (  
		<button onClick={handleClick}>  
			Clicked {count} times  
		</button>  
	);  
}

React will call your component function again. This time, count will be 1. Then it will be 2. And so on.

Using Hooks:

Functions starting with use are called Hooks. useState is a built-in Hook provided by React. You can also write your own Hooks by combining the existing ones. Hooks are more restrictive than other functions. You can only call Hooks at the top of your components (or other Hooks). If you want to use useState in a condition or a loop, extract a new component and put it there.

Sharing data between components:

In the previous example, each MyButton had its own independent count, and when each button was clicked, only the count for the button clicked changed. However, often you will need components to share data and always update together.

To make both MyButton components display the same count and update together, you need to move the state from individual buttons upwards to the closest component containing all of them.

In this example is the App component:

export default function MyApp() {

export default function MyApp() {  
	const [count, setCount] = useState(0);  

	function handleClick() {  
		setCount(count + 1);  
	}  

	return (  
		<div>  
			<h1>Counters that update together</h1>  
			<MyButton count={count} onClick={handleClick} />  
			<MyButton count={count} onClick={handleClick} />  
		</div>  
	);  
}

function MyButton({ count, onClick }) {  
	return (  
		<button onClick={onClick}>  
		Clicked {count} times  
		</button>  
	);  
}

(parameter and argument needs to be same)

The information you pass down like this is called props. Now the MyApp component contains the count state and the handleClick event handler, and passes both of them down as props to each of the buttons.

A list of states:

Remember: To collect data from multiple children, or to have two child components communicate with each other, declare the shared state in their parent component instead. The parent component can pass that state back down to the children via props. This keeps the child components in sync with each other and with their parent.

Lifting state into a parent component is common when React components are refactored.

(some basics of tic-tac-toe):

const [squares, setSquares] = useState(Array(9).fill(null));

This creates an array with nine elements and sets each of hem to null. The useState() call around it declares a squares state variable that’s initially set to that array. Each entry in the array corresponds to the value of a square.

Next you need to change what happens when a Square is clicked. The Board component now maintains which squares are filled. You’ll need to create a way for the Square to update the Board’s state. Since state is private to a component that defines it, you cannot update the Board’s state directly from Square.

Instead, you’ll pass down a function from the Board component to the Square component, and you’ll have Square call that function when a square is clicked. You’ll start with the function that the Square component will call when it is clicked. You’ll call that function onSquareClick:

function handleClick()
{
	const nextSquares = squares.slice()
	nextSquares[0] = 'X';
	setSquares(nextSquares);
}

Why slice? why immutability is important?

Note how you call .slice() to create a copy of the squares array instead of modifying the existing array. There are generally two approaches of changing data. The first approach is to mutate the data by directly changing the data’s values. The second approach is to replace the data with a new copy which has the desired changes.

The result is the same but by not mutating (changing the underlying data) directly, you gain several benefits.

Immutability makes complex features much easier to implement. Later in this tutorial, you will implement a “time travel” feature that lets you review the game’s history and “jump back” to past moves. This functionality isn’t specific to games—an ability to undo and redo certain actions is a common requirement for apps. Avoiding direct data mutation lets you keep previous versions of the data intact, and reuse them later.

Thinking in React

React can change how you think about the designs you look at and the apps you build. When you build a user interface with React, you will first break it apart into pieces called components. Then, you will describe the different visual states for each of your components. Finally, you will connect your components together so that the data flows through them.