20. React and TypeScript#
20.1. What is React#
React
is a popular JavaScript
library for building user interfaces, particularly single-page applications. It allows developers to create reusable UI components and manage the state of their applications efficiently.
20.2. What is TypeScript?#
TypeScript
is a superset of JavaScript
that adds static
types. This allows developers to catch errors at compile time rather than at runtime, making the development process smoother and more reliable.
20.3. Setting Up Your Development Environment#
20.3.1. Install Node.js
#
Node.js
is a JavaScript
runtime that allows you to run JavaScript
code outside of a browser. It also comes with npm
(Node Package Manager), which you’ll use to install libraries and tools.
Download
Node.js
: Go to the Node.js official website and download the LTS version suitable for your operating system.Install
Node.js
: Follow the installation instructions provided for your operating system. After installation, you can verify the installation by running the following commands in your terminal:node -v npm -v
20.3.2. Create a New React Project with TypeScript Using Vite
#
Vite
is a modern frontend build tool that provides a faster and leaner development experience. We’ll use Vite
to create our React
project with TypeScript
.
Open your terminal: Make sure you are in the directory where you want to create your new project.
Run the create command: Use
npm
to create a new project withVite
.npm create vite@4.1.0
This command will prompt you to provide the project name and select the framework and variant.
Project name: Enter the desired name for your project (e.g., my-react-app).
Select a framework: Choose
React
.Select a variant: Choose
TypeScript
Navigate to your project directory:
cd my-react-app
Install dependencies:
npm install
Run the development server:
npm run dev
This will start the development server, and you should see output similar to this:
VITE v4.1.0 ready in 300 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose
20.4. Understanding the Project Structure#
Once your project is created, you will notice several files and folders. Here are the key components:
src/
: This directory contains your source code.App.tsx
: The main application component.main.tsx
: The entry point of your React application.
index.html
: The HTML template for your app.tsconfig.json
: Configuration file for TypeScript.vite.config.ts
: Configuration file for Vite.
20.5. Creating Your First Component#
Let’s create a simple React component to get familiar with the basic concepts.
20.5.1. Create a new file in the src
directory named Hello.tsx
#
// src/Hello.tsx
import React from "react";
interface HelloProps {
name: string;
}
const Hello: React.FC<HelloProps> = ({ name }) => {
return <h1>Hello, {name}!</h1>;
};
export default Hello;
In
TypeScript
, aninterface
is a way to define the structure of an object. It specifies the types of properties that an object can have.In our example, we define an
interface
HelloProps
, Thisinterface
specifies that any object of typeHelloProps
must have aname
property of typestring
.
React components
can be written as eitherclass
components orfunctional
components. Functional components are simpler and often preferred for their conciseness and ease of use.In our example, We declare a
constant
namedHello
. it means thatHello
will always refer to the same component function.This is a common practice in modern JavaScript to ensure that variables and constants are not accidentally reassigned, leading to more predictable and maintainable code.
React.FC<HelloProps>
: We specify thatHello
is a functional component (React.FC
) that takes props of typeHelloProps
.This ensures that TypeScript will enforce the
HelloProps
type for the props passed toHello
.
20.5.2. Modify App.tsx
to include your new component:#
// src/App.tsx
import React from "react";
import Hello from "./Hello";
const App: React.FC = () => {
return (
<div>
<Hello name="World" />
</div>
);
};
export default App;
20.6. Creating a ListGroup
Component#
20.6.1. Setting Up Bootstrap
#
Install Bootstrap and its peer dependencies:
npm install bootstrap@5.2.3
Import Bootstrap CSS
Open
src/main.tsx
and add the following line to import theBootstrap
CSS:
import "bootstrap/dist/css/bootstrap.min.css"; import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; import "./index.css"; ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( <React.StrictMode> <App /> </React.StrictMode> );
20.6.2. Creating the ListGroup Component#
Create a new file
src/components/ListGroup.tsx
and define the ListGroup component:class
is a reserved keyword in javascript / typescript. We need to substitute it asclassName
. (Hint:ctrl + D
do multiple-cursor modification)
Add the following into the
ListGroup.tsx
Conditional Rendering
The
getMessage
function checks if there are any items in the list. If the list is empty (items.length === 0
), it returns a paragraph element with the message"No item found"
.Ternary Operator
(&&)
: If the length of items is 0, the<p>No item found.</p>
message is rendered.
import React from "react"; function ListGroup() { let items = ["New York", "San Francisco", "Tokyo", "London", "Paris"]; items = []; const getMessage = () => { return items.length === 0 && <p>No item found.</p>; }; return ( <React.Fragment> <h1>List</h1> {getMessage()} <ul className="list-group"> <li>test</li> {items.map((item) => ( <li key={item} className="list-group-item"> {item} </li> ))} </ul> </React.Fragment> ); } export default ListGroup;
20.6.3. Handle click events#
Using State with
Hooks
React Hooks
allow you to use state and other React features without writing a class.The
useState hook
is particularly useful for managingstate
infunctional components
.We use the
useState hook
to manage the selected index of our list items. By callingsetSelectedIndex
, we update theselectedIndex
, which triggers are-render
of the component with the new state.
Handling Click Events
Handling
click
events in React involves attaching anonClick
event handler to aDOM
element.When a list item is clicked, the arrow function sets the
selectedIndex
to theindex
of the clicked item.
Dynamic Class Names
We use a conditional expression to apply different class names based on whether a list item is selected.
If the current item’s
index
matchesselectedIndex
, we apply theactive
class to highlight the item.
import React, { MouseEvent, useState } from "react";
function ListGroup() {
let items = ["New York", "San Francisco", "Tokyo", "London", "Paris"];
// Hook
const [selectedIndex, setSelectedIndex] = useState(0);
const getMessage = () => {
return items.length === 0 && <p>No item found.</p>;
};
return (
<React.Fragment>
<h1>List</h1>
{getMessage()}
<ul className="list-group">
{items.map((item, index) => (
<li
key={index}
className={
selectedIndex === index
? "list-group-item active"
: "list-group-item"
}
// onClick={() => console.log(item + "been clicked\n" + index)}
onClick={() => {
setSelectedIndex(index);
}}
>
{item}
</li>
))}
</ul>
</React.Fragment>
);
}
export default ListGroup;
20.6.4. Passing data via props#
In this section, we will extend our ListGroup
component to accept data through props
.
This approach allows us to make our component more reusable and flexible by allowing it to receive different sets of data and headings.
Props Interface
In
TypeScript
, we define aninterface
to specify the types ofprops
that a component will receive.This ensures that our
component
is used correctly, with the correct types of data being passed in.
interface ListGroupProps { items: string[]; heading: string; }
Destructuring Props
When we define the
component
, we candestructure
theprops
directly in the function parameter list. This makes the code cleaner and easier to read.Instead of accessing
props.items
andprops.heading
within the component, we destructure these properties directly in the function signature.
function ListGroup({ items, heading }: ListGroupProps) { // Component logic here }
Using the Component
To see the
ListGroup component
in action, we can create aparent component
that passes different sets of data to it.
// src/App.tsx import React from "react"; import Hello from "./Hello"; import ListGroup from "./components/ListGroup"; const App: React.FC = () => { let city_items = ["New York", "San Francisco", "Tokyo", "London", "Paris"]; let cat_items = [ "Maine Coon", "Ragdoll", "Persian", "Abyssinian", "Siamese", ]; return ( <div> <Hello name="World" /> <ListGroup items={city_items} heading="Cities"></ListGroup> <ListGroup items={cat_items} heading="Cats"></ListGroup> </div> ); }; export default App;
20.6.5. Passing functions via Props#
In this section, we’ll focus on how to pass functions
as props
to a component.
This allows a parent component
to control how certain actions are handled when they occur in a child component
.
Function as Prop
In
React
,functions
can be passed asprops
tochild components
. This allows theparent component
to define the behavior for certain actions in the child component.onSelectItem
: Afunction prop
that takes two arguments,heading
anditem
. This function will be called when a list item is clicked.
interface ListGroupProps { items: string[]; heading: string; onSelectItem: (heading: string, item: string) => void; }
Callback Functions
A
callback function
is afunction
passed intoanother function
as an argument. This technique is useful for handling events that occur in child components within the parent component.handleSelectItem
: Thisfunction
is defined in theparent component (App)
. It logs the heading and item to theconsole
whenever it is called.
const handleSelectItem = (heading: string, item: string) => { console.log(`Heading: ${heading}, Item: ${item}`); };
Using the Component
To see the
ListGroup
component in action, we can create aparent component (App)
that passes thehandleSelectItem
function to it.
import React from "react"; import ListGroup from "./components/ListGroup"; const App: React.FC = () => { const city_items = [ "New York", "San Francisco", "Tokyo", "London", "Paris", ]; const cat_items = [ "Maine Coon", "Ragdoll", "Persian", "Abyssinian", "Siamese", ]; const handleSelectItem = (heading: string, item: string) => { console.log(`Heading: ${heading}, Item: ${item}`); }; return ( <div> <ListGroup items={city_items} heading="Cities" onSelectItem={handleSelectItem} /> <ListGroup items={cat_items} heading="Cats" onSelectItem={handleSelectItem} /> </div> ); }; export default App;
20.7. State vs Props#
20.8. Creating a component accept children#
In this section, we’ll focus on how to create a React component
that can accept and render children elements
.
Children Prop
In
React
, thechildren prop
is a special prop that is automatically passed to every component. It contains the content nested inside the component’s opening and closing tags when it is used.children
: Thisprop
is of typeReactNode
, which can represent any renderable React content, includingelements
,strings
,numbers
,fragments
, andarrays
.
interface AlertProps { children: ReactNode; }
Using the Component
<Alert> Hello <span>World</span> </Alert>