100% found this document useful (2 votes)
241 views

Beginning React, 2024

សៀវភៅ "Beginning React, 2024" គឺជាសៀវភៅដែលផ្តោតលើការបង្រៀនពីការអភិវឌ្ឍន៍កម្មវិធីវែប (web applications) ដោយប្រើប្រាស់ React ដែលជាប្រព័ន្ធបង្កើតចំណុចប្រទាក់អ្នកប្រើប្រាស់ (UI library) ដ៏ពេញនិយម។ សៀវភៅនេះគឺល្អសម្រាប់អ្នកចាប់ផ្តើម និងអ្នកដែលចង់រៀនពីការប្រើប្រាស់ React ក្នុងការបង្កើតកម្មវិធីវែបទំនើប។

Uploaded by

EiRsVi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (2 votes)
241 views

Beginning React, 2024

សៀវភៅ "Beginning React, 2024" គឺជាសៀវភៅដែលផ្តោតលើការបង្រៀនពីការអភិវឌ្ឍន៍កម្មវិធីវែប (web applications) ដោយប្រើប្រាស់ React ដែលជាប្រព័ន្ធបង្កើតចំណុចប្រទាក់អ្នកប្រើប្រាស់ (UI library) ដ៏ពេញនិយម។ សៀវភៅនេះគឺល្អសម្រាប់អ្នកចាប់ផ្តើម និងអ្នកដែលចង់រៀនពីការប្រើប្រាស់ React ក្នុងការបង្កើតកម្មវិធីវែបទំនើប។

Uploaded by

EiRsVi
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 135

TABLE OF CONTENTS

Preface
Working Through This Book
Requirements
Contact
Chapter 1: Introduction
Computer Setup
Your First React Application
Explaining the Source Code
Summary
Chapter 2: Working With React Components
Returning Multiple Elements
Rendering to the Screen
Writing Comments in React
Composing Multiple Components as One
Summary
Chapter 3: Making Sense of JSX
Adding the Class Attribute
Summary
Chapter 4: Props and States
Passing Down Multiple Props
Props are Immutable
State in React
Passing State to a Child Component
Using React DevTools to inspect states and props
Summary
Chapter 5: CSS in React
React Inline Styling
CSS Files
CSS Modules
Tailwind CSS
Which One Should You Use?
Summary
Chapter 6: Project 1 - Wizard Form Application
Project Setup
Building the Wizard Form Structure
Summary
Chapter 7: Creating Wizard Form Mechanism
React Conditional Rendering
Partial rendering with a regular variable
Inline rendering with && operator
Inline rendering with conditional (ternary) operator
Hide Inactive Form Steps
Adding the Next Button
Adding the Previous Button
Summary
Chapter 8: Handling Events in React
Storing Form Input With onChange Event Handler
Wizard Form Submission
Summary
Chapter 9: Using Fetch in React
The useEffect Hook
Summary
Chapter 10: Project 2 - Creating a To-do List Application
Using Tailwind CSS
Laying the Application’s Foundation
Summary
Chapter 11: Updating States and Separating the Components
Creating the AddTask Component
Creating the TaskList Component
Updating Task Status
Deleting a Task
Editing a Task
Creating the TaskInput Component
Summary
Chapter 12: The Context API
Adding the Context API
Moving State Functions to the Context API
Consuming the Context API
Updating the TaskList Component
Update Components Reference in App Component
Summary
Chapter 13: Adding HTTP Requests to Persist Data
Creating the Server and Database
Fetching Data from json-server
Updating addTask() function
Updating updateTaskStatus() Function
Updating editTask() Function
Updating deleteTask() Function
Summary
Chapter 14: Adding Filter Function
Creating the TaskFilter Component
Summary
Wrapping Up
The Next Step
About the author
Beginning React
A Step-By-Step Gentle Guide to Learn React for Beginners

By Nathan Sebhastian
PREFACE

The goal of this book is to provide a gentle step-by-step


instructions that will help you see how to develop web
applications using React.

Instead of covering all the theories and concepts of React, I’ll be


teaching you important building blocks of the library, such as
JSX, components, props, states, event handlers, hooks, and how
you can use them together to build web applications.

After finishing this book, you will be confident in building a


front-end application using React.

Working Through This Book


This book is broken down into 14 short chapters, where each
chapter will focus on a specific aspect of React. The last few
chapters will take all you’ve learned in the previous ones so
that you can build a Wizard Form and a To-do List application
using React.

I encourage you to write the code you see in this book and run
them so that you have a sense of what web development with
React looks like. You learn best when you code along with
examples in this book.

A tip to make the most of this book: Take at least a 10-minute


break after finishing a chapter, so that you can regain your
energy and focus.

Also, don’t despair if some concept is hard to understand.


Learning anything new is hard for the first time, especially
something technical like programming. The most important
thing is to keep going.

Requirements
To experience the full benefit of this book, basic knowledge of
HTML, CSS, and JavaScript is required. No previous knowledge
about React is needed as we will start from the very basics.

If you need some JavaScript refresher, you can get my


JavaScript book at https://codewithnathan.com/beginning-
modern-javascript

Contact
If you need help, you can contact me at
nathan@codewithnathan.com and let me know what obstacles
you are having.

You might also want to subscribe to my 7-day free email course


called Mindset of the Successful Software Developer at
https://sebhastian.gumroad.com/l/mindset
The email course would help you find the right path forward as
a software developer.
CHAPTER 1: INTRODUCTION

React is a very popular JavaScript front-end library that has


received lots of love from developers around the world for its
simplicity and fast performance.

React was initially developed by Facebook as a solution to front


end problems they are facing:

▪ DOM manipulation is an expensive operation and should be


minimized

▪ No library specialized in handling front-end development at


the time (there is Angular, but it’s an ENTIRE framework.)

▪ Using a lot of vanilla JavaScript turns a web application into


a mess, hard to maintain.

Why developers love React? As a software developer myself, I


can think of a few reasons why I love it:

It’s minimalist. React takes care of only ONE thing: the user
interface and how it changes according to the data you feed
into it. React makes your interface dynamic with minimal code.
It has a low learning curve. The core concepts of React are
easy to learn, and you don’t need months or 40 hours of video
lectures to learn about them.

It’s unopinionated. React can be integrated with lots of


different technologies. On the front-end, you can use different
libraries to handle Ajax calls (Axios, Superagent, or just plain
Fetch.) On the back-end, You can use PHP/ Ruby/ Go/ Python or
whatever language you prefer.

Strong community support. To enhance React’s capabilities,


open source contributors have built an amazing ecosystem of
libraries that enables us to make even more powerful
applications. But most open source libraries for React are
optional. You don’t need to learn them until you mastered React
fundamentals.

The bottom line is that with a low learning curve, React gives
you incredible power in making your UI flexible, reusable, and
spaghetti-free.

Learning React opens tremendous opportunities if you want to


work as a web developer.

Computer Setup
To start programming with React, you need to have three
things:

1. A web browser
2. A code editor
3. Node.js
We’re going to use the Chrome browser to run our JavaScript
code, so if you don’t have one, you can download it here:

https://www.google.com/chrome/

The browser is available for all major operating systems. Once


the download is complete, install the browser on your
computer.

Next, we need to install a code editor. There are several free


code editors available on the Internet, such as Sublime Text,
Visual Studio Code, and Notepad++.

Out of these editors, my favorite is Visual Studio Code, because


it’s fast and easy to use.

Installing Visual Studio Code


Visual Studio Code or VSCode for short is an application created
for the purpose of writing code. Aside from being free, VSCode
is fast and available on all major operating systems.

You can download Visual Studio Code here:

https://code.visualstudio.com/

When you open the link above, there should be a button


showing the version compatible with your operating system as
shown below:
Figure 1. Install VSCode

Click the button to download VSCode, and install it on your


computer.

Now that you have a code editor installed, the next step is to
install Node.js

Installing Node.js
Node.js is a JavaScript runtime application that enables you to
run JavaScript outside of the browser. We need to install this
application on our computer to install packages required in
React development.

You can download and install Node.js from https://nodejs.org.


Pick the recommended LTS version because it has long-term
support. The installation process is pretty straightforward.
To check if Node has been properly installed, type the command
below on your command line (Command Prompt on Windows
or Terminal on Mac):

node -v

The command line should respond with the version of the


Node.js you have on your computer.

Your First React Application


It’s time to run your first React application. First, create a folder
on your computer that will be used to store all files related to
this book. You can name the folder 'beginning_react'.

The next step is to open your terminal and run the npm
command to create a new React application using Vite.

Vite (pronounced 'veet') is a build tool that you can use to


bootstrap a new React project. Inside the 'beginning_react'
folder, you need to run the following command to create a new
React Project with Vite:

npm create vite@5.1.0 my-react-app -- --template react

You should see npm asking to install a new package (create-vite)


as shown below. Proceed by typing 'y' and pressing Enter:

Need to install the following packages:


create-vite@5.1.0
Ok to proceed? (y) y

Then Vite will create a new React project named 'my-react-app'


as follows:
Scaffolding project in /Users/nsebhastian/dev/beginning_react/my-react-app...

Done. Now run:

cd my-react-app
npm install
npm run dev

When you’re done, follow the next steps you see in the output
above, use cd command to change the working directory to the
application we’ve just created, then run npm install to install
the packages required by the application.

Then, we need to run the npm run dev command to start our
application:

$ npm run dev

> my-react-app@0.0.0 dev


> vite

VITE v5.0.10 ready in 509 ms

➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help

Now you can view the running application from the browser, at
the designated localhost address:
This means you have successfully created your first React app.
Congratulations!

Explaining the Source Code


Now that you’ve successfully run a React application, let’s take a
look at the source code generated by Vite to understand how
things work.

Run the Visual Studio Code you’ve installed in the previous


section, and open the 'my-react-app' folder inside VSCode.

Here, you should see several folders and files that make up the
React application as follows:
The vite.config.js is a configuration file that instructs Vite on
how to run the application. Because we have a React
application, you’ll see the React plugin imported inside:

import { defineConfig } from 'vite'


import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})

When you run the npm run dev command, Vite will look into this
file to know how to run the program.
The package.json file stores the information about the project,
including the packages required to run the project without any
issues. The package-lock.json file keeps track of the installed
package versions.

The .eslintrc.cjs file contains ESLint rules. ESLint is a code


analysis tool that can identify problematic code in your project
without needing to run the project. It will report any errors and
warnings in VSCode.

The index.html file is a static HTML document that’s going to be


used when running the React application, and the README.md file
contains an introduction to the project.

You don’t need to modify any of these files. Instead, let’s go to


the src/ folder where the React application code is written.

src
├── App.css
├── App.jsx
├── assets
│ └── react.svg
├── index.css
└── main.jsx

First, the App.css file contains the CSS rules applied to the
App.jsx file, which is the main React application code.

The assets/ folder contains the assets required for this project.
In this case, it’s the React icon, which you had seen in the
browser.

The index.css file is the root CSS file applied globally to the
application, and the main.jsx file is the root file that access the
index.html file to render the React application. Here’s the
content of main.jsx file:

import React from 'react'


import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)

Here, you can see that the ReactDOM library creates a root at
the <div> element that contains the root ID, then renders the
whole React application to that element.

You can open the App.jsx file to view the React code:

import { useState } from 'react'


import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

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

return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.jsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}

export default App

In this file, a single component named App is defined. The Vite


and React logos are rendered with a link to the respective
library, and there’s a counter button that will increment the
counter by 1 when you click on it.

This file is where we will be exploring the fundamentals of


React. Let’s delete everything in this file, and write a simple App
component that renders a <h1> element:

function App() {
return <h1>Hello World</h1>
}

export default App

Next, delete the index.css, app.css, and assets/ folder. You also
need to delete the import './index.css' statement in your
main.jsx file.

If you open the browser again, you should see a single heading
rendered as follows:
Alright! Now we’re ready to learn the fundamentals of React.
Let’s start our first lesson in the next chapter.

Summary
In this chapter, we’ve explored the origins of React, and why
developers love to use it.

We’ve also installed the required tools to code a React


application and learn how to generate a barebones React
application using Vite.

Finally, we went through the source code generated by Vite to


know how the application works. If you encounter any issues,
you can email me at nathan@codewithnathan.com and I will do
my best to help you.
In the next chapter, you’re going to start learning about React
components.
CHAPTER 2: WORKING WITH
REACT COMPONENTS

In React, a component is a single independent unit of a user


interface (UI). What you write inside a component will
determine what should appear on the browser screen at a given
time.

In the previous chapter, we’ve created an App component that


returns a heading element:

function App() {
return <h1>Hello World</h1>
}

export default App

A component is made up of a function that returns a single UI


element.

When you want a component to render nothing, you can return


a null or false instead of an element.

function App() {
return null
}
All React components are saved under the .jsx file extension, as
you can see in this project that you have main.jsx and App.jsx.

What is JSX? It’s an extension of JavaScript that produces


JavaScript powered HTML elements. We’re going to learn about
it later.

Returning Multiple Elements


A component must always return a single element. When you
need to return multiple elements, you need to wrap all of them
in a single element like a <div>:

function App() {
return (
<div>
<h1>Hello World!</h1>
<h2>Learning to code with React</h2>
</div>
)
}

export default App

But this will make your application render one extra <div>
element in the browser. To avoid cluttering your application,
you can render an empty tag <> like this:

function App() {
return (
<>
<h1>Hello World!</h1>
<h2>Learning to code with React</h2>
</>
)
}

export default App


With this, you won’t render an extra element.

Rendering to the Screen


To render a React component into the browser, you need to
create a root React component using the ReactDOM library,
which you’ve seen previously when viewing the main.jsx file.

You need to have an HTML file as the source from which your
React component is rendered. Usually, a very basic HTML
document with a <div> is enough, as you can see in the
index.html file:

<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>

Next, you render the component into the <div> element. Notice
how ReactDOM is imported from react-dom package, and the
document.getElementById('root') is used to select the <div>
element below:

import React from 'react'


import ReactDOM from 'react-dom/client'

function App() {
return <h1>Hello World!</h1>
}

ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)
Here, you can see that the App component is placed in the same
file as the ReactDOM library. This can be done if you want to
remove the App.jsx file, so you have only a single main.jsx file
as the source for your React application.

But it’s confusing to have multiple components in one file, so


let’s not do this.

Writing Comments in React


Writing comments in React components can be done just like
how you comment in regular JavaScript code. You can use the
double forward slash syntax // to comment any code.

The following example shows how to comment the export


statement:

function App() {
return (
<>
<h1>Hello World!</h1>
<h2>Learning to code with React</h2>
</>
)
}

// export default App

When you want to comment the code inside the return


statement, you need to use the curly brackets, forward slash
and asterisk format {/* comment here */} as shown below:

function App() {
return (
<>
<h1>Hello World!</h1>
{/* <h2>Learning to code with React</h2> */}
</>
)
}

It may seem very annoying that you need to remember two


different ways of commenting when writing React application.
But don’t worry because a modern tool like VSCode knows how
to generate the right comment syntax.

You only need to press the comment shortcut, which is CTRL + /


for Windows/ Linux or Command + / for macOS.

Composing Multiple Components as One


Up until this point, you’ve only rendered a single App
component to the browser. But applications build using React
can be composed of tens or hundreds of components.

Composing components is the process of forming the user


interface by using loosely coupled components. It’s kind of like
making a house out of Lego blocks, as I will show you in the
following example:

export default function ParentComponent() {


return (
<>
<UserComponent />
<ProfileComponent />
<FeedComponent />
</>
);
}

function UserComponent() {
return <h1> User Component </h1>;
}

function ProfileComponent() {
return <h1> Profile Component </h1>;
}

function FeedComponent() {
return <h1> Feed Component</h1>;
}

From the example above, you can see how the


<ParentComponent> renders three children components:

<UserComponent>

<ProfileComponent>

<FeedComponent>

The composition of many components will form a single tree of


React components in a top-down approach.

The tree would then be rendered into the DOM through the
ReactDOM.render() method:
By composing multiple components, you can split the user
interface into independent, reusable pieces, and develop each
piece in isolation.

Summary
In this chapter, you’ve learned what are React components and
how you can use them to render HTML elements to the screen.
You’ve also seen an example of building a more complex user
interface by composing multiple components as one.
In the next chapter, you’re going to learn more about JSX, the
templating language created for React.
CHAPTER 3: MAKING SENSE OF
JSX

In the previous chapter, you’ve learned that a component must


always have a return statement that contains elements to
render on the screen:

function App() {
return <h1>Hello World</h1>
}

The tag <h1> looks like a regular HTML tag, but it’s actually a
special template language included in React called JSX.

JSX is a syntax extension that produces JavaScript powered


HTML elements. It can be assigned to JavaScript variables and
can be returned from function calls. For example:

function App() {
const myElement = <h1>Hello World</h1>
return myElement
}

Because of JSX, you can also embed JavaScript expression inside


an element by using curly brackets {}:
const lowercaseClass = 'text-lowercase';
const text = 'Hello World!';
const App = <h1 className={lowercaseClass}>{text}</h1>;

This is what makes React element different from HTML


element. You can’t embed JavaScript directly by using curly
braces in HTML.

Instead of creating a whole new templating language, you just


need to use JavaScript functions to control what is being
displayed on the screen.

For example, let’s say you have an array of users that you’d like
to show:

const users = [
{ id: 1, name: 'Nathan', role: 'Web Developer' },
{ id: 2, name: 'John', role: 'Web Designer' },
{ id: 3, name: 'Jane', role: 'Team Leader' },
]

You can use the map() function to loop over the array:

function App() {
const users = [
{ id: 1, name: 'Nathan', role: 'Web Developer' },
{ id: 2, name: 'John', role: 'Web Designer' },
{ id: 3, name: 'Jane', role: 'Team Leader' },
]

return (
<>
<p>The currently active users list:</p>
<ul>
{
users.map(function(user){
// returns Nathan, then John, then Jane
return (
<li> {user.name} as the {user.role} </li>
)
})
}
</ul>
</>
)
}

Inside React, you don’t need to store the return value of the
map() function in a variable. The example above will return a
<li> element for each array value into the component.

While the above code is already complete, React will trigger an


error in the console, saying that you need to put "key" prop in
each child of a list (the element that you return from map()
function):

A prop (short for property) is an input that you can pass to a


component when rendering that component. The key prop is a
special prop that React will use to determine which child
element has been changed, added, or removed from the list.

You won’t use it actively in any part of your array rendering


code, but React will ask for one when you render a list.

It is recommended that you put the unique identifier of your


data as the key value. In the example above, you can use the
user.id data. Here’s how you pass a key prop for each <li>
element:
return (
<li key={user.id}>
{user.name} as the {user.role}
</li>
)

When you don’t have any unique identifiers for your list, you
can use the array index value as the last resort:

users.map(function(user, index){
return (
<li key={index}>
{user.name} as the {user.role}
</li>
)
})

Props are one of the ingredients that make a React component


powerful. You’re going to learn more about it in the next
chapter.

Adding the Class Attribute


You can add the class attribute to your elements by using the
className keyword:

function App() {
return <h1 className='text-lowercase'>Hello World!</h1>
}

The keyword class is reserved for JavaScript classes, so you


need to use className instead.

Summary
In this chapter, you’ve learned that JSX enables you to write
plain JavaScript code to serve as the template for your React
application.

JSX is plain JavaScript, so you can use all valid JavaScript syntax
inside a component.

In the next chapter, we’re going to look at props and states.


These will help you to create dynamic React components.
CHAPTER 4: PROPS AND STATES

Props and states are used to pass data inside React components.
Props (or properties) are inputs passed down from a parent
component to its child component. On the other hand, states are
variables defined and managed inside the components.

Let’s start by understanding props. Suppose you have a


ParentComponent that renders a ChildComponent like this:

function ParentComponent() {
return <ChildComponent />
}

You can pass a prop from ParentComponent into ChildComponent


by adding a new attribute after the component name.

In the code below, the name prop with the value 'John' is passed
to the ChildComponent:

function ParentComponent() {
return <ChildComponent name='John' />
}

When the component is rendered on the browser, the


ChildComponent will receive the name prop into the component.
You can access the props object by defining it in the function
component’s argument:

function ChildComponent(props){
return <p>Hello World! my name is {props.name}</p>
}

The props parameter will always be an object, and any prop you
define when rendering the component will be passed as a
property to the object.

Passing Down Multiple Props


You can pass as many props as you want into a single child
component. Just add the props when using the component as
shown below:

function ParentComponent() {
return (
<ChildComponent
name="John"
age={29}
hobbies={["read books", "drink coffee"]}
occupation="Software Engineer"
/>
)
}

All the props above will be passed to the ChildComponent’s


props parameter.

You can even pass a function into props like this:

function ParentComponent() {
function greetings() {
return 'Hello World'
}
return <ChildComponent greetings={greetings} />
}

In the child component, you can call the function as follows:

function ChildComponent(props) {
return <p>{props.greetings()}</p>
}

Please note that if you pass anything other than a string as a


prop value, you need to put the value in curly brackets.

This is because JavaScript expressions can’t be processed by JSX


unless you put the expression inside curly brackets.

Props are Immutable


Immutable means that a prop’s value can’t be changed no
matter what happens.

In the code below, the ChildComponent tries to change the value


of props.name property:

function ChildComponent(props){
props.name = 'Mark';
return <p>Hello World! my name is {props.name}</p>
}

function ParentComponent() {
return <ChildComponent name='John'/>
}

export default ParentComponent

But you’ll get an error in the console as follows:


Uncaught TypeError: Cannot assign to read only property 'name' of object '#
<Object>'

As you can see, React props can’t be changed once you declare
them. But what if your data needs to change as a user interacts
with your application? This is where state comes to the rescue.

State in React
In React, states are arbitrary data that you can declare and
manage in your components. To create a state in React, you
need to call the useState hook as shown below:

import { useState } from 'react'

function ParentComponent() {
const [name, setName] = useState('John')

export default ParentComponent

In React, hooks are functions that allow you to tap into the
features provided by React. The useState hook is a function that
enables you to put value into the state mechanism.

When calling the useState() function, you can pass an


argument that will serve as the initial value of the state. The
function then returns an array with two elements.

The first element holds the state value, and the second element
is a function that allows you to change the state value. You need
to use the destructuring array syntax to receive both elements
as shown above
You can give any names to the variables returned by useState,
but it’s recommended to use [something, setSomething]

To render the state value, you can embed it into JSX as follows:

function ParentComponent() {
const [name, setName] = useState('John')

return <h1>Hello {name}</h1>


}

If you want to change the value of the name variable, you need to
use the provided setName() function.

But you can’t call setName() in the component’s body, because


React will refresh itself anytime you change the state value.

Instead, you can create a button that will change the value of
name when you click it:

function ParentComponent() {
const [name, setName] = useState('John')

return (
<>
<h1>Hello {name}</h1>
<button onClick={() => setName('Mark')}>Change Name</button>
</>
)
}

In the code above, we create a <button> element and add the


onClick prop, which gets executed anytime we click on the
button.

Inside the prop, we pass a function that simply calls the


setName() function, changing the state value.
Passing State to a Child Component
You can pass the state into any child component, and when you
need to update the state from a child component, you need to
pass the setSomething function received from the useState hook.

Here’s an example of passing a state from ParentComponent to


ChildComponent:

function ParentComponent() {
const [name, setName] = useState('John')

return <ChildComponent name={name} setName={setName} />


}

In the ChildComponent, you can call the setName() function from


props like this:

function ChildComponent(props) {
return (
<>
<h1>Hello {props.name}</h1>
<button onClick={() => props.setName('Mark')}>Change Name</button>
</>
)
}

When the button on the ChildComponent is clicked, the value of


the name state will change. Internally, React will refresh the
application and reflect the changes in the user interface.

Using React DevTools to inspect states and


props
To help ease your development, you can install the React
Developer Tool (DevTool for short) to inspect the current state
and props value of your components. You can install React
DevTool for Chrome at
https://chromewebstore.google.com/detail/react-developer-
tools/fmkadmapgofadopljbjfkapdkoienihi

Once installed, open the developer tool and you should have
two extra tabs called Components and Profiler as shown below:

Similar to how you can inspect CSS rules applied to HTML


elements, you can inspect the state and props of React
components using the developer tool. Click the Components tab,
and inspect one of the two components we created earlier.

Below, you can see the props and state of the ParentComponent, as
well as other details:
When you click on the button, the state value will change
accordingly. You can inspect the ChildComponent to view its
details. This DevTool will come in handy when you develop
React applications.

Summary
In this chapter, you’ve learned about props and states in React.
Both props and states allow you to create arbitrary variables
and pass data between React components.

Props are immutable data passed from in a top-down approach,


from the parent component to the child component. States are
data that you can change, created using the useState hook.

In most cases, states are initialized at the top level component


and passed down as props into children components. To change
the value of a state, you need to call the setSomething() function
returned by the useState hook.

If you need a child component to be able to change state value,


you can pass the setSomething() function to the child
component as a prop.

In the next chapter, you’re going to learn about using CSS in


React.
CHAPTER 5: CSS IN REACT

There are 4 common ways you can add CSS in a React


application:

1. Inline styling
2. CSS files
3. CSS modules
4. Tailwind CSS

This chapter will explore these 4 different ways to write CSS in


React components, and which one you should use when starting
a React application.

React Inline Styling


React components are composed of JSX elements. But just
because you’re not writing regular HTML elements doesn’t
mean you can’t use the old inline style method.

The only difference with JSX is that inline styles must be written
as an object instead of a string. See the example below:
function App() {
return (
<h1 style={{ color: 'red' }}>Hello World</h1>
);
}

In the style attribute above, the first set of curly brackets is used
to write JavaScript expressions. The second set of curly brackets
initialize a JavaScript object.

Style property names that have more than one word are
written in camelCase instead of using the traditional
hyphenated style. For example, the usual text-align property is
written as textAlign in JSX:

function App() {
return (
<h1 style={{ textAlign: 'center' }}>Hello World</h1>
);
}

Because the style attribute is an object, you can also separate


the style by writing it as a constant. This way, you can reuse the
style in other elements as needed:

const pStyle = {
fontSize: '16px',
color: 'blue'
}

export default function App() {


return (
<>
<p style={pStyle}>Hello World!</p>
<p style={pStyle}>The weather is sunny today.</p>
</>
)
}
If you need to extend your paragraph style further down the
line, you can use the spread operator.

This will let you add inline styles to your already declared style
object. See the <p> element below:

const pStyle = {
fontSize: '16px',
color: 'blue'
}

export default function App() {


return (
<p style={{ ...pStyle, color: 'green', textAlign: 'right' }}>
When you go to work, bring your umbrella with you!
</p>
)
}

JSX inline styles allow you to write CSS directly into your
component.

One of the benefits of using the inline style approach is that you
will have a simple component-focused styling technique. When
using an object for styling, you can extend your style by
spreading the object.

But in a big and complex project where you have hundreds of


React components to manage, this might not be the best choice
for you.

You can’t specify pseudo classes using inline styles. That means
you can’t define rules like :hover, :focus, :active, and so on.

Also, you can’t specify media queries for responsive styling.


Let’s consider another way to style your React app.
CSS Files
Another way to add CSS in React is to use .css files. Vite already
knows how to handle a .css file, so all you need to do is import
the CSS file into your JSX file and add the right className prop to
your component.

Let’s create a style.css file in your project folder with the


following content:

/* style.css */
.paragraph-text {
font-size: 16px;
color: #ff0000;
}

Now, let’s import the CSS file into the App.jsx file and add the
class prop to the component:

import './style.css';

function App() {
return (
<p className="paragraph-text">
The weather is sunny today.
</p>
);
}

This way, the CSS will be separated from your JavaScript files,
and you can just write CSS syntax just as usual.

You can even include a CSS framework such as Bootstrap into


React with this approach. All you need to do is import the CSS
file in your root component.
This method will enable you to use all CSS features, including
pseudo classes and media queries.

CSS Modules
A CSS module is a regular CSS file with all of its class and
animation names scoped locally by default.

When applying this method, each React component will have its
own CSS file, and you need to import that CSS file into your
component.

To let React know you’re using CSS modules, name your CSS file
using the [name].module.css convention.

Here’s an example:

/* App.module.css */
.BlueParagraph {
color: blue;
text-align: left;
}
.GreenParagraph {
color: green;
text-align: right;
}

Then import it to your component file:

import styles from "./App.module.css";

function App() {
return (
<>
<p className={styles.BlueParagraph}>
The weather is sunny today.
</p>
<p className={styles.GreenParagraph}>
Still, don't forget to bring your umbrella!
</p>
</>
)
}

When you build your app, Vite will automatically look for CSS
files that have the .module.css name and process the class
names to a new localized name.

Using CSS Modules ensures that your CSS classes are scoped
locally, preventing CSS rules from colliding with each other.

Another advantage of using CSS Modules is that you can


compose a new class by inheriting from other classes that
you’ve written. This way, you’ll be able to reuse CSS code that
you’ve written previously, like this:

.MediumParagraph {
font-size: 20px;
}
.BlueParagraph {
composes: MediumParagraph;
color: blue;
text-align: left;
}
.GreenParagraph {
composes: MediumParagraph;
color: green;
text-align: right;
}

But we’re not going to explore every single feature of CSS


modules here, only enough to get you familiar with it.

Tailwind CSS
Tailwind CSS is a modern utility-first CSS framework that
allows you to style elements by combining a bunch of classes
together.

CSS frameworks like Bootstrap and Bulma provide you with


high-level components that you can immediately use in your
project. When you need to style a button, you just need to apply
the classes that contain the desired CSS properties:

<button className="btn btn-primary">Subscribe</button>

When using Bootstrap, the btn class provides a combination of


CSS properties such as padding, color, opacity, font weight, and
so on.

On the other hand, Tailwind gives you utility classes where


each class has only one or two properties:

<button className='px-5 py-2 text-white bg-blue-500 border-2'>


Subscribe
</button>

In the example above, the px-5 is short for padding padding-left


and padding-right, while 5 is a specific size for the paddings.
The text-white applies color: white, the bg-blue-500 applies the
background-color property, and border applies border-width.

To use Tailwind in your project, you need to integrate the


required JavaScript library in your project. Later in this book, I
will show you how to use Tailwind in Vite.

Which One Should You Use?


It depends on the method you feel comfortable with the most. If
you’re working with a team, you need to discuss and agree on
the method you want to apply, because mixing the styles would
make it hard to develop and maintain the application.

Always use only one way to style React components in a specific


project.

As you will practice building a React project in the next chapter,


I recommend you to use CSS files because it’s the easiest way.
You only need to import .css file in your main.jsx file.

Summary
In this chapter, you’ve seen the four common methods that you
can use to apply CSS in React.

In the next chapter, we’re going to create our first web


application using React.
CHAPTER 6: PROJECT 1 -
WIZARD FORM APPLICATION

In this chapter, you’re going to use what you’ve learned about


React so far to build a web application.

The application you’re going to build is a simple Wizard Form.


A Wizard Form is a multi-step form designed to ease the filling
process for a long and complex form.

By showing only a few inputs at a time, users will feel


encouraged to fill in the blank input rather than feeling
overwhelmed and potentially abandoning the form.

The Wizard Form has three steps as shown below:


Figure 2. Wizard Form Demo

By clicking the next button, you can jump into the other parts of
the form. When you click on the submit button, an alert will be
triggered and your inputs will be displayed:
Figure 3. Wizard Form Alert

To keep things simple, the form will only have one input for
each step and you don’t need to validate the data.

Given the demo above, you basically need to break the form
into one parent component and three child components. The
parent component will show the correct form step based on
user clicks.

Let’s start building this application right away.


Project Setup
To start this project, you need to create a new React application
using Vite. Open the terminal and run the following command:

npm create vite@5.1.0 wizard-form -- --template react

Once the project is created, open the project folder in VSCode,


then run npm install and npm run dev from the VSCode
command line.

Building the Wizard Form Structure


Before thinking how to put the wizard form mechanism in
place, we need to create the base components for the
application.

Based on the brief, we need one parent component and three


child components, so open the App.jsx file and write the
following code:

import { useState } from 'react';

function App() {
const [currentStep, setCurrentStep] = useState(1);
const [email, setEmail] = useState('');
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
}

export default App;

Here, we create several states to keep track of important data in


our application. The active step will be stored in the currentStep
state, while the user’s email, username, and password will be
stored in their respective states.
Next, write the return statement for the App component as
follows:

function App() {
// ...
return (
<div className='container'>
<h1>React Wizard Form ♂</h1>
<p>Step {currentStep} </p>

<form>
<Step1 />
<Step2 />
<Step3 />
</form>
</div>
);
}

The component has a <form> element that wraps the three child
components.

Below the App() function, write the Step1, Step2, and Step3 child
components as shown below:

function Step1() {
return <h1>Step 1 Component</h1>;
}

function Step2() {
return <h1>Step 2 Component</h1>;
}

function Step3() {
return <h1>Step 3 Component</h1>;
}

Now let’s fill these step components with the form inputs:

function Step1() {
return (
<div className='form-group'>
<label htmlFor='email'>Email address</label>
<input
className='form-control'
id='email'
name='email'
type='text'
placeholder='Enter email'
/>
</div>
);
}

function Step2() {
return (
<div className='form-group'>
<label htmlFor='username'>Username</label>
<input
className='form-control'
id='username'
name='username'
type='text'
placeholder='Enter username'
/>
</div>
);
}

function Step3() {
return (
<div className='form-group'>
<label htmlFor='password'>Password</label>
<input
className='form-control'
id='password'
name='password'
type='password'
placeholder='Enter password'
value={props.password}
onChange={props.handleChange}
/>
</div>
);
}
Now if you run the application on the browser, you can see the
skeleton HTML structure finished:

It’s time to add some CSS to beautify this application. Open the
index.css created by Vite and replace the style rules there with
the following one:

body {
margin: 1rem;
font-family: sans-serif;
background-color: #edceb0;
}

.container {
margin-left: auto;
margin-right: auto;
background: #fff;
height: 424px;
width: 338px;
padding: 71px 93px 0;
border-radius: 10px;
box-shadow: 0 2px 7px 0 rgba(0, 0, 0, 0.1);
}
label {
display: inline-block;
margin-bottom: 0.5rem;
}

.form-group {
margin-bottom: 1rem;
}

.form-control {
width: 100%;
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
color: #495057;
border: 1px solid #ced4da;
border-radius: 0.25rem;
box-sizing: border-box;
}

The body style is used to add space, font family, and background
to the application. The .container style is used to beautify the
div containing the form. The label, .form-group, and .form-
control is used to make the form look good.

You can see the style applied in the browser:


We’ve added the three components required for this
application. The foundation is finished.

By the way, don’t forget to delete the App.css file and assets/
folder because we don’t need them.

Summary
In this chapter, we laid the foundations for our Wizard Form
Application by creating the components and styling them.
If you need to check your work, you can get the source code for
this chapter at https://github.com/nathansebhastian/react-
wizard-form-1

In the next chapter, we’re going to see how to perform


conditional rendering in React so that we can display only one
step component at a time.
CHAPTER 7: CREATING WIZARD
FORM MECHANISM

In this chapter, we’re going to implement the Wizard Form


mechanism using React. To do so, we need to know how to
conditionally render components first.

This is doable by specifying a condition when you want to


render a component.

React Conditional Rendering


You can control what output is being rendered by a component
by implementing a conditional rendering in your JSX.

For example, let’s say you want to switch between rendering


the login and logout buttons, depending on the availability of
the user state:

function App(props) {
const { user } = props

if (user) {
return <button>Logout</button>
}
return <button>Login</button>
}
export default App

You don’t need to add an else statement in the component


because React will stop further process once it reaches a return
statement.

In the example above, React will render the logout button when
the user value is truthy, and the login button when user is falsy.

Partial rendering with a regular variable


When developing with React, there will be cases where you
want to render a part of your UI dynamically in a component.

In the example below, the JSX element is stored in a variable


called button, and that variable is used again in the return
statement:

function App(props) {
const { user } = props

let button = <button>Login</button>

if (user) {
button = <button>Logout</button>
}

return (
<>
<h1>Hello there!</h1>
{button}
</>
)
}
Instead of writing two return statements, you store the dynamic
UI element inside a variable and use that variable in the return
statement.

This way, you can have a component that has static and
dynamic elements.

Inline rendering with && operator


It’s possible to render a component only if a certain condition is
met and render null otherwise.

For example, let’s say you want to render a dynamic message


for users when they have new emails in their inbox:

function App() {
const newEmails = 2

return (
<>
<h1>Hello there!</h1>
{newEmails > 0 &&
<h2>
You have {newEmails} new emails in your inbox.
</h2>
}
</>
)
}

In this example, the <h2> element only gets rendered when the
newEmails count is greater than 0.

Inline rendering with conditional (ternary)


operator
It’s also possible to use a ternary operator in order to render the
UI dynamically.

Take a look at the following example:

function App(props) {
const { user } = props

return (
<>
<h1>Hello there!</h1>
{ user? <button>Logout</button> : <button>Login</button> }
</>
)
}

Instead of using a variable to hold the <button> element, you


can simply use the ternary operator on the user value and
render 'Logout' or 'Login' button according to the variable’s
value.

Hide Inactive Form Steps


Back to our Wizard Form application, we need to pass the
currentSteps prop into the child components and use the prop
value to determine whether we want to show the component on
the screen.

First, you need to pass the currentStep prop to the child


components:

function App() {
// ...
<form>
<Step1 currentStep={currentStep} />
<Step2 currentStep={currentStep} />
<Step3 currentStep={currentStep} />
</form>
}

Next, go into the Step1() function and check the currentSteps


value with an if statement.

If the value is anything other than 1, we don’t need to render


the form input. Remember how to render nothing to the
screen?

function Step1(props) {
if (props.currentStep !== 1) {
return null;
}
return (
// ...
);
}

When the currentStep doesn’t match the form step, return a


null to render nothing.

You need to do the same for Step2() and Step3() as follows:

function Step2(props) {
if (props.currentStep !== 2) {
return null;
}
return (
// ...
);
}

function Step3(props) {
if (props.currentStep !== 3) {
return null;
}
return (
// ...
);
}

Once you save the changes, you should only see the first step on
the screen.

Now you need to add buttons so that the user can move from
one step to the other.

Adding the Next Button


To allow the user to move to the next step, you need to create a
button, and when that button is clicked, increment the
currentStep value by 1.

To do so, you can create the button in the <form> element as


follows:

<form>
<Step1 currentStep={currentStep} />
<Step2 currentStep={currentStep} />
<Step3 currentStep={currentStep} />
<button
className='btn btn-next f-right'
type='button'
onClick={() => setCurrentStep(prevStep => prevStep + 1)}
>
Next
</button>
</form>

When the button is clicked, we’re going to call the


setCurrentStep() function. In React, the setSomething() function
always receive the previous state value when called.
To increment the currentStep state value, we make use of the
previous state, which we name prevStep, and increment it by 1.

You can try the button on the browser, but notice that if you
click Next after the third step, the form will be empty.

Because we only have three steps, we need to prevent the user


from clicking the Next button when the third form is active.

We can do so by adding a condition when rendering the Next


button. Right below the useState calls in your App() function,
create a function called nextButton() with the following content:

function App() {
// useState...

const nextButton = () => {


if (currentStep < 3) {
return (
<button
className='btn btn-next f-right'
type='button'
onClick={() => setCurrentStep(prevStep => prevStep + 1)}
>
Next
</button>
);
}
return null;
};
}

Here, we render the Next button only when the currentStep


value is less than 3.

Now we need to call the nextButton() function in place of the


button we created below the form steps:
<Step1 currentStep={currentStep} />
<Step2 currentStep={currentStep} />
<Step3 currentStep={currentStep} />
{nextButton()} // Render the Next button

So far so good. The next step is to add the Previous button so the
user can go back to the previous step.

Adding the Previous Button


To create the Previous button, you can copy the nextButton()
function and tweak it a little:

const previousButton = () => {


if (currentStep > 1) {
return (
<button
className='btn btn-previous'
type='button'
onClick={() => setCurrentStep(prevStep => prevStep - 1)}
>
Previous
</button>
);
}
return null;
};

In the code above, the previousButton() function checks if the


value of currentStep is greater than 1 before rendering the
button.

When the button is clicked, we decrement the state value by


one.

Now call this function in the App component, just below the
nextButton() call:
<Step1 currentStep={currentStep} />
<Step2 currentStep={currentStep} />
<Step3 currentStep={currentStep} />
{nextButton()}
{previousButton()} // Render the Previous button

And that’s it. If you test the application now, you can move
between the form steps, and the button is rendered only when
the condition is right.

The last thing we need to do is to add style to the buttons using


CSS. Right now, they look pretty bad.

Add the following styles to the index.css file:

.f-right {
float: right !important;
}

.btn {
color: #fff;
cursor: pointer;
font-size: 1rem;
line-height: 1.5;
border-radius: 0.25rem;
padding: 0.375rem 0.75rem;
border: 1px solid transparent;
}

.btn-next {
border-color: #007bff;
background-color: #007bff;
}

.btn-previous {
border-color: #6c757d;
background-color: #6c757d;
}
The .f-right style adds the float property to the element to
move it to the right, the .btn is used to style the buttons.

The .btn-next and .btn-previous styles add border and


background colors to the respective elements. Now the buttons
look good.

Summary
In this chapter, you’ve seen how you can conditionally render
components based on the state values. By creating a state and
passing it to the child components, you can determine whether
the component should render something to the screen.

This is one of the reasons developers love using React. You write
code in a declarative way, specifying the conditions for change
in your application.

By just changing a state value, the entire application would


notice and respond accordingly based on the code you’ve
written.

If you need the source code for this chapter, you can get it at
https://github.com/nathansebhastian/react-wizard-form-2

In the next chapter, you’re going to learn about handling events


in React and finish the Wizard Form.
CHAPTER 8: HANDLING EVENTS
IN REACT

Under the hood, React has an internal event handler that


connects to the native DOM event.

This is why we can add the onClick prop to buttons in the


previous chapters, which gets executed in response to a click
event.

When you call a function in response to events, the event object


will be passed to the callback function as follows:

function App() {
const handleClick = (event) => {
console.log("Hello World!");
console.log(event);
}
return (
<button onClick={handleClick}>
Click me
</button>
)
}

When you click on the button above, the event variable will be
logged as a SyntheticBaseEvent object in your console:
Figure 4. React’s SyntheticBaseEvent Log

Whenever a DOM event is triggered, that synthetic event will be


handled by React so that you can decide what to do with that
event.

The use case for this Synthetic event is the same as the native
DOM event. You can respond to user interactions like clicking,
hovering, focusing or typing on a form input, submitting a
form, and so on.

We’ve seen how to handle user clicks before. Now we will see
how to respond to user inputs and form submissions.

Storing Form Input With onChange Event


Handler
Back to the Wizard Form, we need to handle the data the user
typed into our form inputs using React state. This is why we
have email, username, and password states in the first place.

Before we pass the states to our child components, let’s create a


function named handleChange() that will be called each time a
user types into the inputs.

The function will receive the event parameter, and will update
the right state based on the input’s name attribute.

function App() {
// ...useState

const handleChange = event => {


const { name, value } = event.target;
switch (name) {
case 'email':
setEmail(value);
break;
case 'username':
setUsername(value);
break;
case 'password':
setPassword(value);
break;
default:
break;
}
};
}

There are only a limited number of states you need to change,


so using a switch statement is perfect for managing state
changes.

Instead of sending setEmail to Step1 and setUsername to Step2,


you can just send the handleChange function to all child
components.

Let’s do that now:

<form>
<Step1
currentStep={currentStep}
handleChange={handleChange}
email={email}
/>
<Step2
currentStep={currentStep}
handleChange={handleChange}
username={username}
/>
<Step3
currentStep={currentStep}
handleChange={handleChange}
password={password}
/>
{previousButton()}
{nextButton()}
</form>

Next, go to each child component and use the given props on


these components. You only need to add the value and onChange
props to the <input> elements.

See the comment below:

function Step1(props) {
if (props.currentStep !== 1) {
return null;
}
return (
<div className='form-group'>
<label htmlFor='email'>Email address</label>
<input
className='form-control'
id='email'
name='email'
type='text'
placeholder='Enter email'
value={props.email} // set value as email state
onChange={props.handleChange} // the handleChange()
/>
</div>
);
}

Whenever you type or delete a character from the input, the


onChange event will be triggered.

The handleClick() function will then update the state based on


the name attribute of the <input> element as shown above.

You need to add the same props to Step2 and Step3 as follows:
// Step2
<div className='form-group'>
<label htmlFor='username'>Username</label>
<input
className='form-control'
id='username'
name='username'
type='text'
placeholder='Enter username'
value={props.username}
onChange={props.handleChange}
/>
</div>

// Step3
<div className='form-group'>
<label htmlFor='password'>Password</label>
<input
className='form-control'
id='password'
name='password'
type='password'
placeholder='Enter password'
value={props.password}
onChange={props.handleChange}
/>
</div>

Now all child component inputs are connected to the state


value. The last step is to handle the data submission.

Wizard Form Submission


To handle form submission, you need to create a handleSubmit()
function that receives an Event object.

Based on the demo, we only need to call the alert() function to


show the user inputs. Add this function below the
handleChange() function:
const handleSubmit = event => {
event.preventDefault();
alert(`Your registration detail: \n
Email: ${email} \n
Username: ${username} \n
Password: ${password}`);
};

Next, we need to pass this function to the <form> element as


follows:

<form onSubmit={handleSubmit}>
</form>

We have the submit process ready, but notice that we didn’t


have a button to run the process.

We only want the user to be able to submit the form in the third
step of the form, so let’s add a submit button to Step3 as follows:

function Step3(props) {
// ...
return (
<>
<div className='form-group'>
{ /* ...Label and Input */}
</div>
<button className='btn btn-submit f-right'>Sign up</button>
</>
);
}

Here, the submit button is added below the <div> element. We


also add the wrapper <> .. </> so that only one element is
returned by the component.

Now the form is finished. You can test the application in the
browser.
But notice that the form inputs are still there after you submit
the data. To reset the form, let’s add several function calls to
reset the state as follows:

const handleSubmit = event => {


event.preventDefault();
alert(`Your registration detail: \n
Email: ${email} \n
Username: ${username} \n
Password: ${password}`);
// Functions to reset the form
setEmail('');
setUsername('');
setPassword('');
setCurrentStep(1);
};

And that’s it. The Wizard Form is now finished.

You can check the completed application at


https://github.com/nathansebhastian/react-wizard-form-3

Summary
Congratulations on finishing your first React project! The
Wizard Form application helps you see how to build a web
application using React.

Although a Wizard Form looks complex, it’s actually pretty easy


when you have the steps mechanism implemented in React.

By building this project, you’ve seen how an application can be


built step-by-step and gradually becomes more complex.

Always begin a project by finding out what the result looks like,
and then make your way step by step, adding code one block at
a time.
In the next chapter, we’re going to learn how to send and
receive data from a backend service in React. See you there.
CHAPTER 9: USING FETCH IN
REACT

Modern web applications tend to have a modular architecture,


where the back end is separated from the front end. The front
end app will need to send an HTTP network request to a remote
endpoint.

React doesn’t tell you how you should send network requests.
The library only focuses on rendering UI with data
management using props and states.

To fetch data using React, you can use any valid JavaScript
library like Axios, Superagent, and even the native Fetch API.

In this chapter, we’re going to see how to do network requests


using Fetch in React.

The useEffect Hook


When you create a React application that needs to synchronize
with a system outside of React, you need to use the useEffect
hook.
This hook allows you to run some code after rendering so that
you can synchronize your component with some system outside
of React.

When the hook has finished performing the data request, you
can set the response into your component states and render the
appropriate components based on the state values.

To show you an example, let’s fetch data from


https://jsonplaceholder.typicode.com/todos/1 which is a dummy
end point:

function App() {
const [title, setTitle] = useState('');

useEffect(() => {
getData();
}, []);

const getData = async () => {


const response = await
fetch('https://jsonplaceholder.typicode.com/todos/1');
const task = await response.json();
console.log(task)
setTitle(task.title);
};

return <h1>{title}</h1>;
}

In the code above, we create an App component that has a state


called title, and we run the Fetch API to get a todo task from
the API.

When a response is received, we parse the JSON string into a


JavaScript object, log the object, and then set the title state to
the task.title property value.
The response is as follows:

Here, you can see that the console.log() is called twice. This is
because the <React.StrictMode> wrapper always runs a
useEffect hook twice to help you in development.

If you remove the <React.StrictMode> wrapper in main.jsx, the


useEffect hook will run only once.

The useEffect hook itself is a function that accepts two


arguments:

A callback function to run on every render


▪ An array of state variables to watch for changes. useEffect


will be skipped if none of the variables are updated.

When you want to run useEffect only once, you can pass an
empty array as the second argument to the function, as shown
in the example above.

Note that when you use a framework such as Next.js, you


probably don’t need to create an effect hook manually as shown
below.

But it’s still good to know how React works outside of a


framework, because you might work with a technology stack
that uses a non-JavaScript framework such as Rails or Laravel.

Summary
In this chapter, you’ve seen how a React application can send
network requests using the native Fetch API.

React doesn’t tell you what library to use to fetch data. Instead,
it only exposes a hook that enables you to synchronize your
component to external systems.

In the next project, we’re going to see how to use the useEffect
hook when building an application.
CHAPTER 10: PROJECT 2 -
CREATING A TO-DO LIST
APPLICATION

As the final project of this book, we’re going to create a To-do


List application where we can add, edit, delete, and mark a task
as completed.

The finished application looks as follows:


To start building this application, we need to open the
command line and use Vite to create a new React application:

npm create vite@5.1.0 todo-list -- --template react

As before, you can remove the App.css and assets/ folder


because we’re not going to need it.

Using Tailwind CSS


We’re also going to learn how to use Tailwind CSS in a React
project, so you need to install Tailwind libraries.

Open the command line inside the todo-list folder, and run the
following commands:

npm install -D tailwindcss postcss autoprefixer


npx tailwindcss init -p

The npm install command will install the packages required to


use Tailwind, and npx tailwindcss init will initialize the
configuration required to process Tailwind classes added to the
project.

Open the tailwind.config.js file and fill the content parameter


with the location of your JSX files:

/** @type {import('tailwindcss').Config} */


export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

The next step is to add the @tailwind directives for each of


Tailwind’s CSS categories to the ./src/index.css file:

/* index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
You are now ready to use Tailwind in your application. Open
the App.jsx file and create a simple heading component to test
this:

export default function App() {


return (
<h1 className="text-3xl font-bold text-teal-500">
Hello world!
</h1>
)
}

Run the application with npm run dev and you should see the
heading element styled with Tailwind styles:

Very nice. Now you can start developing the components.

Laying the Application’s Foundation


The application will need several icons to make the buttons
appear distinct. There’s a library called React Icons that
provides many publicly available icons.

To use the library, install it with npm first:

npm install react-icons

Now we can import the icons to the App.jsx file like this:

import { FaCheckSquare } from 'react-icons/fa';


import { BsCheck2Square } from 'react-icons/bs';
import { TbRefresh } from 'react-icons/tb';
import { FaRegEdit } from 'react-icons/fa';
import { RiDeleteBin7Line } from 'react-icons/ri';

In your App component, create a state named tasks as follows:

const [tasks, setTasks] = useState([


{
title: 'Learn JavaScript',
completed: true,
id: 1,
},
{
title: 'Learn React',
completed: false,
id: 2,
},
]);

This task state will serve as the dummy data for our tasks until
we connect the backend server with our application.

For now, a local state would be enough so that we can develop


the front-end part faster.

Inside the App component, write a return statement and add the
task title part as follows:
return (
{/* Task Title */}
<div className='max-w-lg px-5 m-auto mt-20'>
<h1 className='flex justify-center text-3xl font-bold underline'>
My Todo List
<FaCheckSquare style={{ color: '#42C239' }} />
</h1>
</div>
)

Here, we simply create an <h1> element wrapped inside a <div>


element. We also use the <FaCheckSquare> component imported
from the React Icons library.

The next step is to add the form so that users can enter their
tasks:

return (
<>
{/* Task Title */}
{/* ... */}
{/* Add Task */}
<form>
<div className='flex items-center w-full max-w-lg gap-2 p-5 m-auto'>
<input
type='text'
placeholder='start typing ...'
className='w-full px-5 py-2 bg-transparent border-2 outline-none
border-zinc-200 rounded-xl placeholder:text-zinc-500 focus:border-zinc-700'
/>
<button
type='submit'
className='px-5 py-2 text-white bg-blue-500 border-2 border-
transparent rounded-lg hover:bg-blue-700'
>
Submit
</button>
</div>
</form>
</>
);
There’s nothing special with the elements above, except that we
add the wrapper <> .. </> to wrap the two components we
have. The class name is long because we’re using Tailwind CSS.

Below the <form> element, create an <ul> element where we will


iterate over the tasks array and show the tasks as a list. We add
three buttons in the element so that users can delete, edit, and
mark a task as completed.

return (
<>
{/* Task Title */}
{/* ... */}
{/* Add Task */}
{/* ... */}
{/* Task List */}
<ul className='grid max-w-lg gap-2 px-5 m-auto'>
{tasks.map(task => (
<li
key={task.id}
className={`p-5 rounded-xl bg-zinc-200 ${
task.completed? 'bg-opacity-50 text-zinc-500':''
}`}
>
<div className='flex flex-col gap-5'>
<span
style={{
textDecoration: task.completed ? 'line-through' : 'none',
}}
>
{task.title}
</span>
<div className='flex justify-between gap-5'>
<button>
{task.completed ? (
<span className='flex items-center gap-1 hover:underline'>
<TbRefresh />
Set Active
</span>
) : (
<span className='flex items-center gap-1 hover:underline'>
<BsCheck2Square />
Set Completed
</span>
)}
</button>
<div className='flex items-center gap-2'>
<button
className='flex items-center gap-1 hover:underline'
>
<FaRegEdit />
Edit
</button>
<button
className='flex items-center gap-1 text-red-500
hover:underline'
>
<RiDeleteBin7Line />
Delete
</button>
</div>
</div>
</div>
</li>
))}
</ul>
</>
);

There are also three conditional renderings in this <ul>


element. One for the <li> element to make the background less
opaque when the task is completed:

<li
key={task.id}
className={`p-5 rounded-xl bg-zinc-200 ${
task.completed? 'bg-opacity-50 text-zinc-500':''
}`}
>
</li>

One to cross out the task title when the task is completed:

<span
style={{
textDecoration: task.completed ? 'line-through' : 'none',
}}
>
{task.title}
</span>

And one for setting the button text between 'Set Active' and 'Set
Completed'

<button>
{task.completed ? (
<span className='flex items-center gap-1 hover:underline'>
<TbRefresh />
Set Active
</span>
) : (
<span className='flex items-center gap-1 hover:underline'>
<BsCheck2Square />
Set Completed
</span>
)}
</button>

You can save the changes and run the application to see that we
have the application foundation done.

Summary
You have put together the foundation for the To-do List
application. If you encounter any error, you can check the
source code used in this chapter at
https://github.com/nathansebhastian/react-todo-1

In the next chapter, you’re going to learn how to add, remove,


and update the tasks data.
CHAPTER 11: UPDATING STATES
AND SEPARATING THE
COMPONENTS

Now that we have a dummy state for the tasks data, the next
step is to add functionalities to update the state so that we can
add, remove, and update the values.

Before proceeding with the development, let’s install two more


packages required for this application:

npm install react-hot-toast nanoid

The react-hot-toast is a package that enables you to create a


toast notification, you can see an example demo at https://react-
hot-toast.com

The nanoid package is used to create a random and unique


string ID. This will be used every time we add a new task to the
state.

We’re also going to separate the components into smaller pieces


of the interface to make it easier to maintain.
Inside the src/ folder, create a new folder named components.
Inside the folder, create a new JSX file named AddTask.jsx.

Creating the AddTask Component


The AddTask component will host the input and the button
required to add a new task to the state.

We need a state to manage the input value, which serves as the


title of the new task:

import { useState } from 'react';

export const AddTask = ({setTasks}) => {

const [title, setTitle] = useState('');

return (
<form>
<div className='flex items-center w-full max-w-lg gap-2 p-5 m-auto'>
<input
type='text'
placeholder='start typing ...'
className='w-full px-5 py-2 bg-transparent border-2 outline-none
border-zinc-200 rounded-xl placeholder:text-zinc-500 focus:border-zinc-700'
value={title}
onChange={event => setTitle(event.target.value)}
/>
<button
onClick={handleSubmit}
type='submit'
className='px-5 py-2 text-white bg-blue-500 border-2 border-
transparent rounded-lg hover:bg-blue-700'
>
Submit
</button>
</div>
</form>
);
};
Most of the code for AddTask is copied from the <form> you
created in the App component earlier.

One difference is that you need to create a state for the title
value, and then add the value and onChange props to the <input>
element.

Here, there’s also a handleSubmit() function that will handle the


form submission. Above the return statement, write this
function:

const handleSubmit = event => {


event.preventDefault();
if (title.trim() !== '') {
addTask(title);
} else {
toast.error('Task field cannot be empty!');
}
};

Inside this function, we need to call the preventDefault()


method first to stop the default HTML behavior of submitting
data to the same URL.

An if condition checks whether the title is not empty. When


the title is empty, the toast.error() method is called to notify
the user of an error.

You need to import the react-hot-toast library at the top of the


file:

import { useState } from 'react';


import { toast } from 'react-hot-toast';
Now, you need to create the addTask function, which is called
when the title is not empty. Add this function above the
handleSubmit function:

const addTask = title => {


const newTask = {
title: title,
completed: false,
id: nanoid()
};

setTasks(prevTasks => [...prevTasks, newTask])


setTitle('')
toast.success('New Task added!')
};

First, you create a newTask object and input the title, completed,
and id properties. The id value is filled using the nanoid() so
that each task has a unique identifier.

After that, call the setTasks() function to add the newTask object.
If you recall, the setSomething() function returned by the
useState hook always receives the previous state value.

Since the tasks state is an array, you can use the spread
operator to expand the array, then add the newTask object next
to it:

setTasks(prevTasks => [...prevTasks, newTask])

And that’s it. Once the task is added, call setTitle() to reset the
input and show a toast to the user.

Import the nanoid function from the nanoid library at the top of
the file:
import { nanoid } from 'nanoid';

Let’s add this component to the App component next. First,


create a file named index.jsx in your components/ folder and
add the following export statements:

// index.jsx
export { AddTask } from './AddTask';

This file is created so that you can import the component


without having to specify the full path.

In the App.jsx file, import the component as follows:

import { AddTask } from './components';

Without the index.jsx file, you need to specify the full path as
'./components/AddTask' at the from part.

Next, use the AddTask component in place of the <form> element


like this:

{/* Task Title */}


<div className='max-w-lg px-5 m-auto mt-20'>
<h1 className='flex justify-center text-3xl font-bold underline'>
My Todo List
<FaCheckSquare style={{ color: '#42C239' }} />
</h1>
</div>
{/* Add Task */}
<AddTask setTasks={setTasks} />

Oh, and we also need to add the <Toaster> component from the
react-hot-toast package so that the toast will show on the
screen.
Add the component above the task title as follows:

<Toaster position='bottom-right' />


{/* Task Title */}

You can test the application on the browser and see if you can
add a new test to it.

Creating the TaskList Component


The next step is to update your <ul> element so that you can
edit, delete, and mark a task as completed.

To prevent the App.jsx file from going too long, let’s separate
this component into its own file named TaskList.jsx in the
components/ folder.

Write some code into the file as follows:

import { BsCheck2Square } from 'react-icons/bs';


import { TbRefresh } from 'react-icons/tb';
import { FaRegEdit } from 'react-icons/fa';
import { RiDeleteBin7Line } from 'react-icons/ri';
import { toast } from 'react-hot-toast';
import { useState } from 'react';

export const TaskList = ({ tasks, setTasks }) => {


// TODO: write the component logic
}

Most of the import statements for React Icons are moved here,
so you can remove the same imports from the App.jsx file.

Open the index.jsx file and export the TaskList component as


follows:
export { AddTask } from './AddTask';
export { TaskList } from './TaskList';

Now you can import TaskList in App like this:

import { useState } from 'react';


import { FaCheckSquare } from 'react-icons/fa';
import { AddTask, TaskList } from './components';
import { Toaster } from 'react-hot-toast';

function App() {
// useState
return (
<>
<Toaster position='bottom-right' />
{/* Task Title */}
{/* ... */}
<AddTask setTasks={setTasks} />
<TaskList tasks={tasks} setTasks={setTasks} />
</>
);
}

When using the TaskList component, don’t forget to pass the


tasks and setTasks as props.

Updating Task Status


The next step is to write the logic of the component. Going back
to the TaskList.jsx file, let’s handle updating task status first
because that is the easier one.

Create a new function named updateTaskStatus() with the


following content:

const updateTaskStatus = taskId => {


const updatedTasks = tasks.map(task => {
if (task.id === taskId) {
task.completed = !task.completed;
}
return task;
});
setTasks(updatedTasks);
toast.success('Task status updated!');
};

The code above uses the Array map() method to iterate over the
tasks and find the task whose id value matches the task we
want to update.

Once found, we invert the completed value using the NOT !


operator, so true becomes false and vice versa.

Then, we call the setTasks() function to update the state and the
toast.success() method to notify the user.

Next, We need to add this function to the return statement. Let’s


copy the task list <ul> element that we have in the App.jsx file as
the return value of this component first:

return(
<ul className='grid max-w-lg gap-2 px-5 m-auto'>
{tasks.map(task => (
<li
key={task.id}
className={`p-5 rounded-xl bg-zinc-200 ${
task.completed? 'bg-opacity-50 text-zinc-500':''
}`}
>
<div className='flex flex-col gap-5'>
<span
style={{
textDecoration: task.completed ? 'line-through' : 'none',
}}
>
{task.title}
</span>
<div className='flex justify-between gap-5'>
<button>
{task.completed ? (
<span className='flex items-center gap-1 hover:underline'>
<TbRefresh />
Set Active
</span>
) : (
<span className='flex items-center gap-1 hover:underline'>
<BsCheck2Square />
Set Completed
</span>
)}
</button>
<div className='flex items-center gap-2'>
<button
className='flex items-center gap-1 hover:underline'
>
<FaRegEdit />
Edit
</button>
<button
className='flex items-center gap-1 text-red-500
hover:underline'
>
<RiDeleteBin7Line />
Delete
</button>
</div>
</div>
</div>
</li>
))}
</ul>
)

Now we need to add an onClick prop to the <button> element


that wraps the 'Set Active' and 'Set Completed' span elements:

<button onClick={() => updateTaskStatus(task.id)}>


{task.completed ? (
<span className='flex items-center gap-1 hover:underline'>
<TbRefresh />
Set Active
</span>
) : (
<span className='flex items-center gap-1 hover:underline'>
<BsCheck2Square />
Set Completed
</span>
)}
</button>

And that’s it. You should be able to toggle the task status now.

Deleting a Task
The next step is to create the delete functionalities. Let’s write a
function that removes a matching task in the TaskList
component:

const deleteTask = taskId => {


const confirmation = confirm('Are you sure?');
if (confirmation) {
setTasks(prevTasks => prevTasks.filter(task => task.id !== taskId));
toast.success('Task Deleted!');
}
}

The deleteTask() function will create a confirmation dialogue,


and when the user confirms, the setTasks() function will be
called.

In the setTasks() function, the previous state value will be


filtered using the filter() function, removing the task with a
matching id value.

Once finished, notify the user by running the toast.success()


method.

Add this deleteTask() function to the button that we created for


deletion:
<button
onClick={() => deleteTask(task.id)}
className='flex items-center gap-1 text-red-500 hover:underline'
>
<RiDeleteBin7Line />
Delete
</button>

Now you should be able to delete a task from the list.

Editing a Task
The final step is to implement the edit function. Let’s create two
states that will help in editing a component:

export const TaskList = ({ tasks, setTasks }) => {


// States to control edit button
const [editTaskId, setEditTaskId] = useState(null);
const [editTaskTitle, setEditTaskTitle] = useState('');
}

The editTaskId will store the id of the task we want to edit, and
the editTaskTitle will store the new title for the task.

Next, create a function that will update the edit state:

const handleEditTask = (taskId, taskTitle) => {


setEditTaskId(taskId);
setEditTaskTitle(taskTitle);
}

Here, the handleEditTask will set the editTaskId and


editTaskTitle value. This is done so React knows when to open
the edit state.
Add the handleEditTask() function to the onClick prop of the
edit button. It’s the one that wraps the <FaRegEdit /> icon:

<button
onClick={() => handleEditTask(task.id, task.title)}
className='flex items-center gap-1 hover:underline'
>
<FaRegEdit />
Edit
</button>

When the edit state is active, an input box and a button to


update the task will be shown in place of the task.

To do so, you need to create a conditional rendering with the


ternary operator as follows:

<ul>
{editTaskId === task.id ? (
<div className='flex gap-2'>
<input
type='text'
className='w-full px-5 py-2 bg-transparent border-2 outline-none
border-zinc-200 rounded-xl placeholder:text-zinc-500 focus:border-zinc-700'
value={editTaskTitle}
onChange={event => setEditTaskTitle(event.target.value)}
/>
<button
className='px-5 py-2 text-white bg-green-500 border-2 border-
transparent rounded-lg hover:bg-green-700'
onClick={() => editTask(task.id)}
>
Update
</button>
</div>
) : (
// the whole <li> element
)}
</ul>
By using the ternary operator, you can render the edit state
only when the editTaskId contains a valid id value.

The <input> element is used to update the editTaskTitle value,


and the <button> is used to update the task.

You need to define the editTask() function as follows:

const editTask = taskId => {


const updatedTasks = tasks.map(task => {
if (task.id === taskId) {
task.title = editTaskTitle;
}
return task;
});
setTasks(updatedTasks);
setEditTaskId(null);
setEditTaskTitle('');
toast.success('Task title updated!');
}

In this function, the map() method is used once again to iterate


over the tasks state value, and the task with a matching id will
have its title value updated to the editTaskTitle value.

Next, we call the setTasks() function to update the state, and we


reset the editTaskId and editTaskTitle state values.

Once finished, we run the toast.success() method to notify the


user of the update.

Creating the TaskInput Component


While the core functionality is complete, there’s still a little
room for improvements in our application.
Notice how the <input> component in the AddTask and TaskList
components are almost identical:

// AddTask.jsx
<input
type='text'
placeholder='start typing ...'
className='w-full px-5 py-2 bg-transparent border-2 outline-none border-
zinc-200 rounded-xl placeholder:text-zinc-500 focus:border-zinc-700'
value={title}
onChange={event => setTitle(event.target.value)}
/>

// TaskList.jsx
<input
type='text'
className='w-full px-5 py-2 bg-transparent border-2 outline-none border-
zinc-200 rounded-xl placeholder:text-zinc-500 focus:border-zinc-700'
value={editTaskTitle}
onChange={event => setEditTaskTitle(event.target.value)}
/>

Except for the placeholder, value, and onChange props, the


className and type props are the same.

Instead of writing two <input> elements like this, we can create


a single input component that we can reuse in both places.

Create a new component named TaskInput as follows:

export const TaskInput = (props) => {


return (
<input
{...props}
className='w-full px-5 py-2 bg-transparent border-2 outline-none
border-zinc-300 rounded-xl placeholder:text-zinc-500 focus:border-zinc-700'
type='text'
/>
);
};
Here, the TaskInput component simply defines the className
and type props of the <input> component.

To receive props when you use this component, the spread


operator is used to extract the props value from the object:

<input
{...props}
...
/>

Now back in our AddTask and TaskList components, we can call


the TaskInput component and declare just the unique props in
those components:

// AddTask.jsx
import { TaskInput } from './TaskInput';
// ...
<TaskInput
placeholder='start typing ...'
value={title}
onChange={event => setTitle(event.target.value)}
/>

// TaskList.jsx
import { TaskInput } from './TaskInput';
// ...
<TaskInput
value={editTaskTitle}
onChange={event => setEditTaskTitle(event.target.value)}
/>

Now you see how to create a reusable component that does


only one thing in React.

If you need to check your work, you can find the source code
for this chapter at https://github.com/nathansebhastian/react-
todo-2
Summary
You’ve learned a lot in this chapter! By now, you’ve seen how
React states and props can be used to control the values that are
important for the application.

While you can put everything in a single .jsx file, a good React
application is usually split between many components to make
it easier when working with a team.

To remove repetition in React, you can create a component that


does a single thing well, and import it into the components that
require those functionalities.

In the next chapter, we’re going to use the Context API to lift the
tasks state up from local to global.
CHAPTER 12: THE CONTEXT API

The Context API provides a way to pass data into components


without having to pass props manually between components.
The API creates a global storage for states, and any component
that requires the state data would simply access the API.

So far, you’ve used the states and props to pass data in a top-
down approach as follows:

When using the context API, the data will be created and
managed inside the API instead of the top component:
When implemented, the Context API is a component that wraps
your React application (usually called the Provider component).

To get the values from this API, you need to use a special hook
called the useContext hook.

Let’s see how to implement the Context API in our To-do List
application so that it makes sense.

Adding the Context API


The Context API is usually separated from the rest of your
components to make it more maintainable.

First, create a folder named context/ inside the src/ folder then
create a new file named TasksContext.jsx.

Inside the file, create a new context object using the


createContext() function provided by React:
import { createContext } from 'react';

export const TaskContext = createContext();

The createContext() function creates a context object that holds


the context value, you need to import it to components that
want to access the Context value later.

Next, create a TaskProvider that serves as the Provider


component. You also need to initialize the tasks state here
instead of in the App component:

export const TaskProvider = props => {


const [tasks, setTasks] = useState([
{
title: 'Learn JavaScript',
completed: true,
id: 1,
},
{
title: 'Learn React',
completed: false,
id: 2,
},
]);

return (
<TaskContext.Provider>{props.children}</TaskContext.Provider>
);
};

The TaskProvider component returns the TaskContext.Provider


component which you’re going to use in the App component as
follows:

import { FaCheckSquare } from 'react-icons/fa';


import { AddTask, TaskList } from './components';
import { Toaster } from 'react-hot-toast';
import { TaskProvider } from './context/TaskContext';
function App() {
return (
<TaskProvider>
{/* Toaster etc... */}
</TaskProvider>
);
}

When you create a component that wraps a child component,


the components are passed under the children props.

This is why the <TaskContext.Provider> component access the


{props.children} object to render the content of the App
component.

Moving State Functions to the Context API


Now that we have the tasks state inside the TaskProvider, let’s
add the functions used to add, update, and delete tasks inside
the provider as well:

export const TaskProvider = props => {


const [tasks, setTasks] = useState([
// ...
]);

const addTask = title => {


const newTask = {
title: title,
completed: false,
id: nanoid(),
};

setTasks(prevTasks => [...prevTasks, newTask]);


};

const updateTaskStatus = taskId => {


const updatedTasks = tasks.map(task => {
if (task.id === taskId) {
task.completed = !task.completed;
}
return task;
});
setTasks(updatedTasks);
toast.success('Task status updated!');
};

const editTask = (taskId, editTaskTitle) => {


const updatedTasks = tasks.map(task => {
if (task.id === taskId) {
task.title = editTaskTitle;
}
return task;
});
setTasks(updatedTasks);
};

const deleteTask = taskId => {


const confirmation = confirm('Are you sure?');
if (confirmation) {
setTasks(prevTasks => prevTasks.filter(task => task.id !== taskId));
toast.success('Task Deleted!');
}
};
}

Next, we need to make the state and the functions to


manipulate it accessible. Create a value object that refers to the
state and functions just below the deleteTask function as
follows:

const value = {
tasks,
addTask,
updateTaskStatus,
editTask,
deleteTask,
};

Now pass the value object into <TaskContext.Provider>


component as follows:
<TaskContext.Provider value={value}>
{props.children}
</TaskContext.Provider>

The Context API is now finished.

Anything you pass as the value prop in the


<TaskContext.Provider> component will be accessible when
calling the useContext hook, which we’re going to use next.

Consuming the Context API


To consume the TasksContext, you need to import the context
into components that require it.

First, open the AddTask component and import the context as


follows:

import { TaskContext } from '../context/TaskContext';

Inside the AddTask component, replace the addTask() function


with the one from Context API:

export const AddTask = () => {


const { addTask } = useContext(TaskContext);

// ...
}

The useContext hook simply returns the values stored in the


Provider component. Because the AddTask component doesn’t
need the rest of the values, the destructuring assignment is used
to extract only the addTask() function.
Notice that the {setTasks} function extracted from the props
object is removed from the AddTask function argument. The
component no longer needs the prop passed from the parent
component because of the Context API.

If you try to add a new task now, you’ll notice that the title
local state doesn’t reset after you add the task.

This is because the setTitle() function is no longer called


inside the addTask() function. You need to call the function and
run a toast from the handleSubmit() function:

const handleSubmit = event => {


event.preventDefault();
if (title.trim() !== '') {
addTask(title);
// Reset title and toast:
setTitle('');
toast.success('New Task added!');
} else {
toast.error('Task field cannot be empty!');
}
};

The title state is a local state in AddTask component, so you


need to access it here instead of inside Context API.

Updating the TaskList Component


The last step is to update the TaskList component. Import the
TasksContext as you did before, and get the state and functions
needed by this component using the useContext hook.

import { TaskContext } from '../context/TaskContext';


import { useContext } from 'react';
export const TaskList = () => {
const { tasks, deleteTask, editTask, updateTaskStatus } =
useContext(TaskContext);
// ...
}

Now you can delete the deleteTask(), editTask(), and


updateTaskStatus() functions from the component body.

Also notice that the { tasks, setTasks } props are removed


from the TaskList component. You need to remove the props in
App component later.

Because the editTask() function is now imported from the


Context, you need to create a function that will respond to the
Update button click in this component.

Create a runEditTask() function with the following content:

function runEditTask() {
editTask(editTaskId, editTaskTitle);
setEditTaskId(null);
setEditTaskTitle('');
toast.success('Task title updated!');
}

Next, refer to this function in the Update button:

<button
className='...'
onClick={() => runEditTask(task.id)}
>
Update
</button>

The runEditTask() function will reset the local states and create
a toast notification when you click the Update button.
Update Components Reference in App
Component
Because the child components are accessing data directly from
the Context API, you no longer need to pass the props when
using these components.

You can remove the props in the App component:

<TaskProvider>
{/* Update AddTask and TaskList: */}
<AddTask />
<TaskList />
</TaskProvider>

You’ve replaced the local tasks state with the Context API. Very
cool!

If you need the source code for this application to review the
changes, you can get it from
https://github.com/nathansebhastian/react-todo-3

Summary
In this chapter, we’ve seen how we first created a local state in
the App component before replacing it with the Context API.

The Context API is used to create a global storage for React’s


state, enabling the components to access data directly from the
API instead of having it passed down from a parent component.

By implementing the Context API, you have a single source of


data that can be accessed from components no matter how far
down they are from the top App component.
In the next chapter, you’re going to use a remote API to source
the state values, enabling the application to communicate with
a remote data server.
CHAPTER 13: ADDING HTTP
REQUESTS TO PERSIST DATA

While the main functionalities of the application are complete,


the tasks data is still sourced from the state. This means
anytime you refresh the application, the state data will be reset
as it’s not persisted in a database.

In this chapter, we’re going to create a simple JSON file that will
serve as the database for the To-do List. We’re going to learn
how to perform CRUD operations using the Fetch API to create,
read, update, and delete a task.

Let’s create the database first.

Creating the Server and Database


First, create a new file named db.json at the root of your
application folder, then put the following content in it:

{
"tasks": [
{
"title": "Learn Swimming",
"completed": true,
"id": 1
},
{
"title": "Learn Cooking",
"completed": false,
"id": 2
}
]
}

This JSON file will serve as the database of our application. This
file will be updated when we add, update, or delete a task.

The next step is to run this JSON file as a server. To do so, you
need to use Node Package Manager (or NPM for short) to install
the json-server package.

The command I want you to run is npm install -g json-server.


This command will install the json-server package globally on
your computer.

Once the installation is finished, you should see the following


output:
The next step is to run the json-server package. Run this
command in your command line:

json-server db.json

If you see the following output, it means the JSON server is


running successfully:

You can open the browser and access the URL at


http://localhost:3000/tasks to see the JSON data we created
earlier.

The To-do List application we’re going to create is going to


access this API endpoint for fetching, adding, removing, and
updating the tasks.

You’ve created a simple server that exposes an API endpoint


successfully. Nice work!

Fetching Data from json-server


Now that you have a remote API to access the tasks data, you
need to connect to the API anytime you want to add, delete, or
update a task.

As explained in Chapter 9, you need to use the useEffect hook


when accessing an external system from inside React.

You can do this in the TaskContext file you created earlier. First,
create a constant variable that will store the base API URL as
follows:

export const TaskContext = createContext();

const BASE_API_URL = 'http://localhost:3000/tasks';

Next, Add a useEffect hook that will run a getData() function as


follows:

export const TaskProvider = props => {


const [tasks, setTasks] = useState([]);

const getData = async () => {


const tasksJson = await fetch(BASE_API_URL);
const tasks = await tasksJson.json();
setTasks(tasks);
};

useEffect(() => {
getData();
}, []);

// ...
}

Because we’re going to source the state from the data returned
by the json-server, we initialize the tasks state as an empty
array, removing the hard-coded values.
Next, we create the getData() function as an asynchronous
function and call the fetch() function to get the tasks data.

Once we receive the data, we call the .json() method to convert


the JSON string into a JavaScript array of objects. The last step is
to set the data into the state using the setTasks() function.

Now if you run the application, you can see that the tasks data
are retrieved from the db.json file. You need to update the state
functions next.

Updating addTask() function


The addTask() function should now add a new task to the
db.json file instead of updating the state, so you need to refactor
the state as follows:

const addTask = async title => {


const newTask = {
title: title,
completed: false,
id: nanoid(),
};

const response = await fetch(BASE_API_URL, {


method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(newTask),
});

if (response.ok) {
toast.success('Task status updated!');
getData();
}
};
Here, you can see that once we create the newTask object, we call
the Fetch API to send a POST request to add the new task to the
database.

Once the request is finished and we get the ok response, we run


the getData() function to refresh the tasks list.

Updating updateTaskStatus() Function


The updateTaskStatus() function should perform a PATCH
request to update the task status as follows:

const updateTaskStatus = async taskId => {


const taskToUpdate = tasks.find(task => task.id === taskId);

taskToUpdate.completed = !taskToUpdate.completed;

const response = await fetch(BASE_API_URL + '/' + taskId, {


method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(taskToUpdate),
});

if (response.ok) {
toast.success('Task status updated!');
getData();
}
};

The find() method will return a task object that has the same id
value as the one you want to update.

After that, a PATCH request is sent to the API URL, but you also
need to add the id of the task you want to change. This is why
the URL in fetch() is BASE_API_URL/{taskId} as shown above.
Updating editTask() Function
The editTask() function is similar to the updateTaskStatus()
function, except that we’re updating the task title instead of the
completed status:

const editTask = async (taskId, editTaskTitle) => {


const taskToUpdate = tasks.find(task => task.id === taskId);

taskToUpdate.title = editTaskTitle;

const response = await fetch(BASE_API_URL + '/' + taskId, {


method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(taskToUpdate),
});
if (response.ok) {
getData();
}
};

The last one is to update the delete functionality.

Updating deleteTask() Function


The deleteTask() will send a DELETE request as follows:

const deleteTask = async taskId => {


const confirmation = confirm('Are you sure?');
if (confirmation) {
const response = await fetch(BASE_API_URL + '/' + taskId, {
method: 'DELETE',
});

if (response.ok) {
toast.success('Task Deleted!');
getData();
}
}
};

And now the To-Do List application will source the tasks data
from the db.json file.

If you need to check the source code, you can find it at


https://github.com/nathansebhastian/react-todo-4

Summary
When developing an application, you will frequently write
some code that acts as a scaffold. They are there to help you
finish the application, but they will be removed when you have
the completed version.

In the previous chapter, you’ve experienced this when updating


the state from local to using the Context API.

In this chapter, you once again updated parts of the code so that
the tasks data can be sourced from a remote API.

While we used the json-server package to create our database,


the API implementation with the Fetch API is similar if we use a
common backend technology such as Node.js, Firebase, PHP,
Rails, and so on.

The json-server package is used to simplify this tutorial.


Otherwise, we need to set up a real local server, adding
needless complexity to the application.
CHAPTER 14: ADDING FILTER
FUNCTION

We just need the filter function to finish our To-do List


application.

In JSON server, you can filter the returned data by adding a


query parameter. To filter by the completed property value, the
URL is http://localhost:3000/tasks?completed=true or
http://localhost:3000/tasks?completed=false.

You can test this by opening the URL on the browser.

Since we already have a getData() function, we can create a


filterTasks() function that simply calls on getData() inside
TaskContext.jsx as follows:

const filterTasks = filter => {


getData(filter);
}

Then, we add the filterTasks function to the value object:

const value = {
tasks,
addTask,
updateTaskStatus,
editTask,
deleteTask,
filterTasks
};

Now we need to update the getData() function to accept the


filter argument.

If we want to see only completed tasks, then we send the


'completed' value to the getData() function.

To accommodate the filter argument, we need to add a


parameter to the getData() function. Let’s update the function
as shown below:

const getData = async (filter) => {


let parameter = '';
if (filter === 'completed') {
parameter = '?completed=true';
} else if (filter === 'active') {
parameter = '?completed=false';
}

const tasksJson = await fetch(BASE_API_URL + parameter);


const tasks = await tasksJson.json();
setTasks(tasks);
};

Now the getData() function can fetch only active, completed, or


all tasks depending on the value of the filter parameter.

The next step is to create a filter component that will make use
of the filterTasks() function.

Creating the TaskFilter Component


The TaskFilter component will be a simple <select> element
that will show tasks that match the selected status.

The component will make use of the filterTasks() function we


created earlier:

import { useContext } from 'react';


import { TaskContext } from '../context/TaskContext';

export const TaskFilter = () => {


const { filterTasks } = useContext(TaskContext);
};

The return statement of the component will be a <label> and


<select> elements wrapped in a <div> as follows:

return (
<div className='flex justify-end items-center max-w-lg px-5 m-auto my-2'>
<label
htmlFor='filter'
className='w-28 font-medium text-gray-900 text-right pe-2'
>
Filter Tasks:
</label>
<select
id='filter'
className='w-28 bg-gray-50 border border-gray-300 text-gray-900
rounded-lg focus:ring-blue-500 focus:border-blue-500 p-2.5'
defaultValue='All'
onChange={event => filterTasks(event.target.value)}
>
<option value='all'>All</option>
<option value='completed'>Completed</option>
<option value='active'>Active</option>
</select>
</div>
);

Then export the component from the index.jsx file so we can


import it from App later:
export { TaskFilter } from './TaskFilter';

Now that you have the filter component ready, import the
component into the App.jsx file.

Place this component below the AddTask component as follows:

// Update the import statement


import { AddTask, TaskList, TaskFilter } from './components';

// use the TaskFilter component


<AddTask />
<TaskFilter />
<TaskList />

And that’s it. Now you can filter the tasks shown by the
application by using the select component.

To see the full source code of the To-do List application, you can
go to https://github.com/nathansebhastian/react-todo-5

Summary
Congratulations on finishing your second React Application! By
building the To-do List application, you’ve seen how an
application can be build from basic components.

As the application becomes more complex, you can gradually


integrate new features and technologies. In this application,
you’ve learned how to switch from using a local state to using a
Context API as the data manager for your React application.

After creating the application, we use an external system as the


source of the data, and use the Fetch API and json-server to
make the data persistent.
With this experience and knowledge, you can now build more
applications from scratch using React. There really is nothing to
be afraid of React once you’ve learned how its features work
together.
WRAPPING UP

Congratulations on finishing this book! You have learned all of


React’s core concepts and develop two applications using that
knowledge. I hope you have fun learning React as I did writing
this book.

Though the book ends here, your journey to mastering React is


not over yet. It’s time for you to find some inspiration to start
developing complex applications using the library.

The Next Step


Now that you have learned the basics of React, you might want
to learn about Next.js, the framework used to create a full-stack
React application. Using Next.js, you can create both the client-
side and the server-side of your application using React and
JavaScript.

Besides using Next.js, you can also use the MERN stack to create
a full-stack React application.

You might also want to learn about TypeScript, the library used
to add static typing to JavaScript applications.
I’m currently planning to write books on these topics, so you
might consider subscribing to my newsletter to know when I
release the books at https://codewithnathan.com/newsletter

I also have a 7-day free email course on Mindset of The


Successful Software Developer which is free at
https://sebhastian.gumroad.com/l/mindset

Each email unveils a slice of wisdom that I got after working as


a web developer and programmer for 8+ years in the tech
industry, from startups to mega-corporations. It offers a fresh
perspective on what it means to be a happy and successful
software developer.

If you like the book, I would appreciate it if you could leave me


a review in Amazon because it would help others to find this
book.

If there are some things you wish to tell me about this book, feel
free to email me at nathan@codewithnathan.com. I’m very
open to feedback and eager to improve my book so that it can
be a great resource for people learning to code.

If you didn’t like the book, or if you feel that I should have
covered certain additional topics, please let me know in the
email.

I wish you luck on your software developer career onward, and


I’ll see you again in other books.

Until next time!


ABOUT THE AUTHOR

Nathan Sebhastian is a senior software developer with 8+ years


of experience in developing web and mobile applications.

He is passionate about making technology education accessible


for everyone and has taught online since 2018
Beginning React

A Step-By-Step Gentle Guide to Learn React for Beginners

By Nathan Sebhastian

https://codewithnathan.com

Copyright © 2024 By Nathan Sebhastian

ALL RIGHTS RESERVED.

No part of this book may be reproduced, or stored in a retrieval


system, or transmitted in any form or by any means, electronic,
mechanical, photocopying, recording, or otherwise, without
express written permission from the author.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy