Module 1 - React Components and Component States
Intro to React
In this Core Competency, you will delve into the heart and soul of React programming, exploring its fundamental building blocks that empower developers to create powerful and interactive user interfaces.
React (React.js or ReactJS as it is sometimes referred to) is considered to be the new architecture for JavaScript Web Development. A favorite amongst developers worldwide, React is not only a JavaScript library, but a new approach to web design and implementation. It was originally developed - as Facebook still maintains it.
It all started as a desire to build new features over vanilla JS to make it easier for Facebook to create and maintain its complex interface. But eventually, React became a very popular open-source tool, given its modular nature. React can also be considered a framework, as it offers other modules for more advanced applications. You will explore some of them!
Third-party frameworks (collections of practices, modules, and libraries) are also built to work with React. For instance, Next.JS is a popular framework that contains tools for optimizations, data fetching, and server components. But let's not get overwhelmed!
First, you will learn the fundamental concepts of React and how to bootstrap a React app in your local environment. At its core, React revolves around the concept of components. Components are encapsulated units of functionality that allow developers to break down complex user interfaces into smaller, reusable parts. With React, we harness the power of these building blocks to construct dynamic and responsive UIs with ease.
Understanding the React Library
React has quickly become one of the most commonly used libraries for building applications today.
Because you've now seen some code that allows you to build custom components with so-called vanilla JavaScript (JS), you've understood how to use JS to manipulate DOM elements. React will take that knowledge you have learned and abstract away many of the traditional DOM syntax, methods, and properties (e.g., document.getElementByClassname). As you'll see later, your entire application will live within one targeted DOM element. React will handle the rest for you.
User interfaces today are rich and must deal with ever-changing data. As users interact with DOM elements and many animations and events are fired, the DOM is undoubtedly doing a lot of work. Imagine an app like Twitter or Facebook with users clicking everywhere and constantly changing data (including instant status updates). For these types of large-scale applications, React is very helpful, but you can also use React to bootstrap a simple application quickly!
To keep up with today's demands of the web, we need a way to build applications that can take care of much of the work for us. As developers, we should concentrate on the actual business logic of the application instead of worrying about every single element of the page. Think of React as a higher-level programming language, if you will.
Let's face it, working with the DOM API is hard. The React team recognizes this, so they built a simple engine called the virtual DOM that interacts with the actual DOM for us. We tell the virtual DOM what to render to the actual DOM, and it will do so. Beyond that, it will "react" when our app's state (current condition) changes and will update the DOM accordingly. All on its own!
In a process called "reconciliation," React will detect that the app's state has changed. Then it will update the virtual DOM, noting which nodes have changed due to the state changes. Finally, once it knows which nodes have changed, it will update only those specific nodes on the actual DOM. React takes a lot of pressure off our browsers (and significantly reduces the code size), and it's why React is as powerful as it is.
Today, web applications are enormous, complex pieces of software that millions and millions of users interact with simultaneously. React provides a smooth experience for our users, as well as for those developing applications.
How to Build It
As mentioned before, working with React is not as straightforward as starting your project in vanilla JS. Libraries need to be imported, and files must be linked to work together. In theory, you could code a single HTML and a single JS file to make it work, but as you will see, creating an initial file structure will let us think in terms of reusable components and make it easier to write, read and maintain React projects.
There are several 3rd party tools that will create this initial structure (or bootstrap) a React project for you (e.g. create-react-app, nextJs). In the name of consistency, and to avoid breaking code when these tools are updated (or even deprecated), we have decided to create our own Bloomtech bootstrapping tool!
Assuming you have NodeJS and npm installed, all you have to do is open your terminal and run:
npx @bloomtools/react@latest project-name
This will create a directory and all the files you need to practice along!
Go into your recently created directory and then run:
npm install
npm run dev
If all went fine, your browser should now display the demo app! A local web server was automatically launched and the application was made available at localhost:3003
Now if you inspect the directory structure, you will see a lot of files representing the basic React structure.
open another new terminal window,
go into the app directory,
and use your editor:
code .
Bloomtech's React npm package
Let's take a moment to understand some of these files and how they are working to render the page. This knowledge will be important for many Learning Objectives and practices ahead!
If you want to get all the details and even study the code behind the tool, the docs for the npm package are here.
For now, these are the key points:
- Most of the files in the root directory are related to configuration and packages. You can ignore them.
- The backend directory will be used for Server/API related activities in the future. It's also safe to ignore them for now.
- The frontend directory contains the actual files used by the React app.
- index.html and index.js will be loaded as soon as your development server is up. Take a moment to inspect these files:
- index.html is basically creating a header, a div#root, and a footer. This div with the id root is where the whole React App will be rendered!
- You can see in index.js that a bunch of libraries and styles are imported, but most importantly, the DOM div#root is selected to become the React's root object. Right after that, the method render is called with a special syntax to actually render the page and execute the React app. A little bit strange, but you will get used to this notation as you move forward.
Most React apps will have a similar structure.
The components directory contains the bulk of the code of the app. You will soon learn about components and they represent in React.
Expand the components directory and inspect the App.js file.
This is what the page is rendering right now, between the header and the footer.
Go ahead and clean this file so that you can start practicing in the next Learning Objective.
The App.js file should look like this:
import React from 'react'
export default function App() {
return ();
}
Most of time during this React course, whenever you need to bootstrap a new React app, you will be initializing the App.js file just like that.
That's all! You can save the file and verify that the browser has updated. Do not worry about all the other files and details for now.
React Developer Tools Extension for Chrome
This is also a good time to install the React Dev Tools extension for Chrome. This a great tool to inspect and debug React apps, and you will use it frequently throughout this unit.
Just follow the instructions in the link above for now, and we'll return to it soon!
Next, you will learn about React components and start coding!
Working with components
Anatomy of a React Component
React components are the building blocks of the application. Inside the JS file, a component is simply a function, returning a special type of data which looks like HTML. Components are usually created in their own JS files, and are exported so that other JS files (typically the index.js) can import and use them.
The main (root) component is often called App. So go ahead and edit the App.js file like this:
import React from 'react';
const Intro = () => {
return (
<div>
<h1>Hi Bloomtech!</h1>
</div>
);
};
Understand that a "component" is a pretty loose term to describe a discrete chunk of your site. A header could be a component, for example. Or a footer, or a video frame, etc.
This one's pretty simple; you're rendering a div with an h1 inside of it. Go check your local web server, which should have updated automatically when you saved the App.js file. The header, the footnote, and the page styling are defined somewhere else (index.html and index.js).
This component is going to render exactly what you'd expect if you had the exact same code below in your HTML file:
<div>
<h1>Hi Bloomtech!</h1>
</div>
So what's so odd about this? Well, you're mixing two different syntaxes. Part of this block is a regular JavaScript function, and part is HTML. In vanilla JavaScript, you can't have HTML. So if you can't have HTML in JavaScript, then what is that HTML-looking thing?
The HTML inside the return statement is called JSX, or JavaScript Extension. Underneath its disguise, the result of JSX is just a JavaScript object. The React library will parse this return data and translate it into a JS object like this:
import React from 'react';
const Intro = () => {
return (
{
type: 'div',
props: {
children: {
type: 'h1',
props: {
children: "Hi Bloomtech!"
}
}
}
}
);
};
During runtime (i.e., when the application is running), the React library will know precisely how to interpret this object and send proper render data to the DOM.
So when you return what looks like HTML in a React component, you secretly return a JavaScript object that describes the kind of HTML we want to make. But, of course, React will figure out how to make it work.
Early on, it's essential to understand that a React component is just a regular JavaScript function. We could return an object like the one written above, which would work, but we want to use JSX for several reasons. First, it's easier to read than that big nested object. And second, it's going to allow us to put our application's logic where it belongs: directly next to the thing the logic applies to.
In other words, JSX is easier to write, read, debug, maintain, and extend. As long as you understand the basics of the JSX syntax, you should be ready to go!
A brief demonstration of React's power
In the above example, you saw the text hardcoded in the h1 tag to read 'Hi BloomTech!'. But React gives you the ability to control our app's content dynamically. Look at this:
import React from 'react';
const Intro = () => {
const greeting = "Hi BloomTech!";
return (
<div>
<h1>{greeting}</h1>
</div>
);
};
The code changed, but the output is the same. That's because the code is a regular JavaScript function, and you're free to declare a variable the way you would normally do:
const greeting = "Hi Bloomtech!";
Once you're in "JSX mode" (inside the return statement), React gives you the power to escape back into regular JavaScript and refer to that variable by using the curly brackets: <h1>{greeting}</h1>
These curly brackets will evaluate any valid JavaScript expression. So if you change the content of the h1 to <h1>{2 + 2}</h1>
, you get the text "4" in the h1 header. Try it!
Now you know the underlying mechanism of React. You don't want to have to hardcode the content of our HTML; instead, you want to compute it.
By now, some of you are probably thinking, "Computing our markup, isn't that what we learned DOM manipulation for?" It's a great question.
Why do it this way?
We're going to focus on two central pillars of React's design philosophy: the separation of concerns and declarative programming.
Separation of concerns
In computer programming, "the separation of concerns" refers to a design philosophy that each piece of your code should do one and only one thing, as much as possible. Functions with a lot of moving parts are hard to debug. In addition, one large function might be tricky to test. If we can split that function into smaller pieces that are easy to test individually, our application will be more robust and easier to understand.
Think of a button. We can't truly separate a <button>
tag from the function the button invokes. Separating the two (one in an HTML file and another in a JavaScript file) isn't a separation of concerns; it's cutting one concern in half and then placing them apart. It's a headache we don't need.
Consider the difference between creating a button with HTML and JS verses doing it in React
//HTML FILE
<button class="button"></button>
//JS FILE
let button = document.querySelector('.button');
button.addEventListener("click", (data)=>{...logic} )
This works fine if you are using the traditional DOM methods, since you don't want to to clutter your HTML file, or have to come back to it for edits.
But with React, you are doing it all at once inside the JS file:
<button onClick={ () => submitForm(data) } />
Imperative Programming vs Declarative Programming
You have an array. Say you want to iterate over it and double each number. Here are two ways you could go about it.
let myArray = [1,2,3,4,5];
for (i = 0; i < myArray.length; i++) {
myArray[i] = myArray[i] * 2;
}
Or:
let myArray = [1,2,3,4,5];
const double = number => number * 2;
myArray.map(double);
The first approach is an example of imperative code, and this is the kind of approach with which we're most familiar. There's nothing wrong with imperative code, it's very explicit, and all code is ultimately imperative at a lower level. The problem with imperative code is it's pretty tricky, and in more complex examples, it can be hard to understand what the code does at a glance.
The second approach is an example of declarative code. Instead of telling the computer, step by step, how you want it to do something, you just tell it what you want it to do.
myArray.map(double)
means "Map over my array and double everything inside of it."
With practice, declarative code is easier to work with. This is important because, believe it or not, as a programmer, most of your time is spent on something other than writing code. Instead, it's spent reading other people's code and trying to understand what it does. If you can grasp this distinction and appreciate its value, congratulations! You now understand the basis of functional programming, the programming paradigm that React is modeled after.
How to Build It
Let's build a component that displays an emoji. This is how you would do that in React:
export default function App() {
return (
<div className="App">
<p>
😂
</p>
</div>
);
}
Edit your App component and watch the result!
At this point you are not concerned about how to add other components to the page, nevertheless, every single component to be rendered in the page will have this same structure.
Notice how you have className
instead of class
in our JSX. That is because JSX is just JavaScript, and class
is a reserved word in JS.
Inside the component function, before the return statement, you can of course run any JS code. But you can also evaluate JavaScript expressions inside the JSX block: read variables, run functions, read data from objects or arrays, all kinds of things! To do this, you use curly braces: { }.
Here you will use curly braces to render the value of an object property as a header.
const emoji = "😂";
const titleObj = {
title: "Emoji!"
};
export default function App() {
return (
<div className="App">
<h2>{titleObj.title}</h2>
<p>
{emoji}
</p>
</div>
);
};
The first expression evaluates down to the string "Emoji!". And the second expression evaluates down to the emoji string.
JSX is powerful, versatile, and easy to work with!
Conclusion
At this point, you have learned the fundamentals of React, including its JSX extension, and how it is related to components and rendering.
React is great way to build dynamic applications, abstracting a lot of low level JavaScript into components that are easy to read, write and debug.
Moving on, you will learn how to use React to deal with events, manage and pass data across components!
State in React
State management is a vital aspect of building robust and interactive React applications, or most applications in any language, for that matter. The concept of State Management dates back to the first studies about Computer Science, where the computer and its program are often called a State Machine.
State refers to the condition of all data at any given time. When the application is in a steady mode, waiting for some event to decide what to do next and continue, we say that the application is ready to move from one particular state to another.
In this Core Competency, you dive deep into the concept of state and its significance in the React ecosystem. You will explore how React components utilize state to handle dynamic data and UI updates, allowing for seamless user experiences. With practical examples and best practices, you'll gain a comprehensive understanding of state management techniques in React, empowering you to create scalable and responsive applications.
In the past, dynamic pages used to rely on back-end servers to handle data and perform UI updates. The frontend would constantly communicate back and forth with the web server to provide a dynamic experience to the user.
React allows a complete self-contained dynamic application running solely on the user's browser! This not only improves responsiveness but makes it significantly easier to design and maintain apps.
Get ready to unlock the full potential of the state in React and elevate your development skills to new heights.
Working with state
In React, you will often talk and think about "state". But what is state, and why is it so essential?
At a high level, state represents the condition of a system at a particular point in time, which is subject to change as actions are performed. For example, consider a soccer match where each team has scored three goals. In this context, we say the "state" of the game is "tied, 3x3". Similarly, a traffic light alternates between three states: red, yellow, and green, each representing a different condition with corresponding behaviors, and each happening in a pre-defined order.
If you've ever used a to-do list app, think about the various conditions it could be in. You might start with a state of "three to-dos, none of which are completed." Then, as you mark a task as done, you've changed the app's state. This change in state might be reflected visually on the screen, such as by crossing off a completed item or updating the count of remaining tasks.
Managing the state of your application is crucial to providing a responsive and dynamic user experience. The state dictates the behavior and presentation of your app at any given moment. React's state management capability allows developers to track these changes efficiently and update the interface in response, ensuring that the user always interacts with the most current "version" of your application. This is the power of state in React, a fundamental concept you'll soon become intimately familiar with as you journey into this exciting library.
How to Build It
To see what state means concretely, you will build a component that displays an emoji. The emoji will either be happy or sad.
import React from 'react';
const happyEmoji = "😁";
const sadEmoji = "😞";
export default function App() {
return (
<div className="App">
<h1>
{happyEmoji}
</h1>
<h1>
{sadEmoji}
</h1>
</div>
);
}
As you can imagine (and verify in the browser), this doesn't quite get the app where it needs to go. Both emojis are displayed at the same time, just like it would happen if we had the JSX content as part of the HTML file.
Your application has the assets you want, but only one emoji image should show at a time. To achieve this, you will have to keep track of its state.
import React from 'react';
import {useState} from "react";
const happyEmoji = "😁";
const sadEmoji = "😞";
export default function App() {
const [happyOn, setHappy ] = useState();
return (
<div className="App">
<h1>
{happyEmoji}
</h1>
<h1>
{sadEmoji}
</h1>
</div>
);
};
A couple of things have changed here.
First, the code is importing the useState hook from the React library so you can use it. A hook in React is a unique function that lets you "hook into" React state. You will later learn about some other hooks in React!
Second, you've got this bit of syntax, which is configuring the hook:
const [happyOn, setHappy ] = useState();
If this syntax looks strange to you, you're not alone. We'll get back to why it looks like that later. For now, all it's doing is declaring two variables at once. It's doing something close to this:
let happyOn;
let setHappy = (value) => {happyOn = value;};
You may have noticed that happyOn doesn't have an initial value. But as you can see, if you were to invoke setHappy and pass a value in as an argument, you would change the value of happyOn.
setHappy("true");
console.log(happyOn); // "true"
What if we want that variable to be initialized with a value from the get-go? We can do that. Let's change:
const [happyOn, setHappy ] = useState();
To:
const [happyOn, setHappy ] = useState(false);
Now it's sort of like we're saying this:
let happyOn = false;
let setHappy = (value) => {happyOn = value;};
happyOn is a variable whose value is whatever we passed into useState. In this case, its value is the boolean primitive false. setHappy is a function that will change the value of happyOn. We'll also note that we could have named happyOn and setHappy whatever we wanted. We could've called them peanutButter and jelly if we wanted, but that would have made it confusing for someone reading the code to understand what was happening.
Recap State
Okay, so what have you covered so far? You've gone over what state is the current snapshot of an application. You also read how to keep track of a state variable from within a component, initialize the state variable with a value, and change that value. What's missing?
Well, for one, you still have two emojis. You have a state variable initialized and a way to change it, but the emojis don't know the state variable exists. You have to make the emojis aware of what the state is so they know what to do when the state changes. You'll need to know a critically important pattern to make that happen.
What is Conditional Rendering?
Conditional rendering is a fancy name for a prevalent pattern in React. The app should show only one light bulb at a time; you only want to render one or the other based on some condition. In this case, if the happyOn boolean is false, the app shows the sad emoji. If happyOn is true, the app shows the happy one. (this is a straightforward use-case for the ternary operator in JavaScript)
import React from 'react';
import {useState} from "react";
const happyEmoji = "😁";
const sadEmoji = "😞";
export default function App() {
const [happyOn, setHappy] = useState(false);
return (
<div className="App">
{happyOn === false ? <h1>{sadEmoji}</h1> : <h1>{happyEmoji}</h1>}
</div>
);
};
This is the only part that changed:
<div className="App">
{happyOn === false ? <h1>{sadEmoji}</h1> : <h1>{happyEmoji}</h1>}
</div>
The ternary operator acts as a single line if/else statement.
{happyOn === false ? <h1>{sadEmoji}</h1> : <h1>{happyEmoji}</h1>}
It's saying "Is the happyOn state variable set to false? If so render <h1>{sadEmoji}</h1>, otherwise render <h1>{happyEmoji}</h1>."
Remember this pattern; you'll use it a lot. And notice how you can easily change the "HTML" using conditions. That's the power of JSX!
So what's the result of this code? Well, edit your code and find out!
Progress! The app is only showing the sad emoji. What if you want it to show the happy one now? Remember, the ternary operator is conditionally rendering one emoji or the other, so the app will show the sad emoji if happyOn is false, and the happy one if it's true. What happens if the initial value of happyOn is hard-coded to be true?
const [happyOn, setHappy] = useState(true);
Check the result!
By the way, if you don't set the initial boolean value, React will make it true by default.
Victory is within sight! You now have a condition on which to render one emoji or the other. That condition was based on the application's state. When the the application's state was changed, the page updates (or reacts) to that change. That's what React is all about: when the state changes, your application reacts.
The last step is figuring out how to change the state.
Remember our hook from earlier?
const [happyOn, setHappy] = useState(false);
Recall that setHappy is a function that can change the value of happyOn. Now you need a way to invoke this function.
Changing state using events
Now that you have learned the concept of state in React, let's dive into how to change it using events. You're likely already familiar with event listeners, such as click listeners, from your previous JavaScript experience. In React, you'll continue to use them but with a slight twist. If you want to listen for a click event on a React component, you'll use a special event handler called onClick
. Note the camel-case spelling - this is a key aspect of the JSX syntax, which treats event handlers differently than regular HTML.
The onClick
event handler receives a function, which is called a "callback function". React invokes this function when the event occurs, allowing your code to perform various actions, including updating the state. State will change in response to user events, and callback functions provide a flexible means of defining these responses.
Before you practice with state changes, it is essential to understand React's process for dealing with state changes. Every time any state is updated, React will:
- Decide if the page needs to be rendered
- Render the page if needed
- Set the new state
- Re-enter the "waiting for changes" mode again
You must follow some rules when updating state to ensure this process works as it should. Following these rules will avoid issues and debugging time in the future. Here are the rules:
Never change the state variable directly
Instead, use the associated function declared by useSate()
. In our example, if you wanted to update the happyOn
to false
, instead of doing:
happyOn = false;
You would write:
setHappy(false);
Always use the arrow notation when assigning a callback function to an event
Let's say you wanted the div to listen for click events and call setHappy
to update happyOn
to false
:
RIGHT:
<div className="App" onClick={ () => setHappy(false) }>
{happyOn === false ? <h1>{sadEmoji}</h1> : <h1>{happyEmoji}</h1>}
</div>
Perfect! You have correctly assigned a callback function to the click event, defining exactly how to react to the event.
WRONG:
<div className="App" onClick={setHappy}>
{happyOn === false ? <h1>{sadEmoji}</h1> : <h1>{happyEmoji}</h1>}
</div>
All state functions expect an argument!
WRONG:
<div className="App" onClick={setHappy(false)}>
{happyOn === false ? <h1>{sadEmoji}</h1> : <h1>{happyEmoji}</h1>}
</div>
This code will run the function right after the first render, which will, in turn, update the state, trigger another render, and so on. This type of infinite loop can often happen in React if you are not careful.
How to Build It
Update your App.js file with the code below and test it by clicking on the component:
import React from 'react';
import {useState} from "react";
const happyEmoji = "😁";
const sadEmoji = "😞";
export default function App() {
const [happyOn, setHappy] = useState(false);
return (
<div className="App" onClick={ () => setHappy(true) }>
{happyOn === false ? <h1>{sadEmoji}</h1> : <h1>{happyEmoji}</h1>}
</div>
);
};
The emoji should change to happy when you click over it!
There's one last tweak we could make to get our application to work more dynamically. Let's say you wanted to toggle the emoji between happy and sad after each click.
The code above passes true as an argument, so after the first change, the emoji will always be happy no matter how many times you click on it. React is smart enough to understand that the state is not changing, so the app will remain steady, and no DOM renders will be triggered.
Instead of passing false, let's pass the opposite of whatever the state is:
import React from 'react';
import {useState} from "react";
const happyEmoji = "😁";
const sadEmoji = "😞";
export default function App() {
const [happyOn, setHappy] = useState(false);
return (
<div className="App" onClick={ () => setHappy(!happyOn) }>
{happyOn === false ? <h1>{sadEmoji}</h1> : <h1>{happyEmoji}</h1>}
</div>
);
};
The !happyOn
(not happyOn) expression will update the state to the opposite of it currently is.
Conclusion
You have built a fully functional and interactive application with just a few lines of code! That's the power of React.
By simply initializing your project, the project automatically pulls down the libraries and imports the components correctly into index.js for you. Now you can fully concentrate on the component's code instead of configuring your project. By understanding the "React syntax" and respecting some general rules, creating and adding interactive components to your app is easy and fun with React!
Guided Project
React Components and Component State Starter Code
React Components and Component State Solution
Module 1 Project: React Stateful Widgets
This project allows you to practice the concepts and techniques learned in this module and apply them in a concrete project. This module explored React components and component state. During the module, you studied what React is, what React components are and how to build them, what state is and how to make a component stateful, and how to update component state with click handlers. In this project, you will demonstrate proficiency of these subjects and principles by fleshing out several stateful components.
The module project contains advanced problems that will challenge and stretch your understanding of the module's content. The project has built-in tests for you to check your work, and the solution video is available in case you need help or want to see how we solved each challenge, but remember, there is always more than one way to solve a problem. Before reviewing the solution video, be sure to attempt the project and try solving the challenges yourself.
Instructions
The link below takes you to Bloom's code repository of the assignment. You'll need to fork the repo to your own GitHub account, and clone it down to your computer:
Starter Repo: React Stateful Widgets
- Fork the repository,
- clone it to your machine, and
- open the README.md file in VSCode, where you will find instructions on completing this Project.
- submit your completed project to the BloomTech Portal