使用 Typescript 进行反应
目前,React.js 已经成为构建美观且可扩展的用户界面的热门库。今天,我想用 TypeScript 构建一个 React 的演示项目。
项目设置
我们希望使用create-react-app
Facebook 开发者团队构建的环境来创建我们的项目。我们的项目将包含一个表单,用于添加喜爱的电视剧并显示列表。它将是一个单页网站。首先,我们将运行以下命令:
npx create-react-app --template typescript fav-tv-series
cd fav-tv-series
npm run start
第一个命令会创建一个名为“ fav-tv-series
with TypeScript Template”的 React 应用。然后,进入该目录后,运行第三个命令,创建一个在 3000 端口上运行的进程,如下所示:
创建界面
在 TypeScript 中,我们主要关注的是为每个将要使用的数据定义严格的类型。interface
这是一种定义数据并将其用作 ts 类型的好方法。在src
根文件夹的文件夹中,我们将创建一个名为 的目录interfaces
,并在其中创建一个名为 的文件SeriesProps.tsx
。在这里,我们将创建一个名为 的接口,SeriesProps
如下所示
export interface SeriesProps {
seriesList: {
name: string;
imdb: number;
cover: string;
seasons: number;
genre: string;
}[]
}
更新应用程序
首先,我们将App.tsx
通过删除现有代码来更新现有文件。我们的单页 Web 应用程序将包含两个组件。一个是表单,用户可以在其中输入关于自己喜欢的系列的必要信息;另一个是包含这些系列的列表。数据将存储在名为 state 的状态中seriesList
,并借助setSeriesList
方法进行更新。
import React, { useState } from 'react';
import { SeriesProps } from './interfaces/SeriesProps';
import './App.css';
import List from './components/List';
import Form from './components/Form';
function App() {
const [seriesList, setSeriesList] = useState<SeriesProps["seriesList"]>([]);
return (
<div className="App">
<h1>My Favourite TV Series</h1>
<Form seriesList={seriesList} setSeriesList={setSeriesList} />
<List seriesList={seriesList} />
</div>
);
}
export default App;
创建列表
在根文件夹的目录中,src
我们将创建一个名为的目录components
,并在那里创建List.tsx
文件。我们的组件如下所示。
import React, { FC } from "react";
import { SeriesProps } from "../interfaces/SeriesProps";
const List:FC<SeriesProps> = ({seriesList}) => (
<div className="series-list">
{seriesList.map((series) => (
<div className="series-item">
<img src={series.cover} alt="Series-cover" />
<p><b>{series.name}</b></p>
<p>{series.genre}</p>
<p>{series.seasons} seasons</p>
<p>★★★★★ {series.imdb}</p>
</div>
))}
</div>
);
export default List;
这里我们来看一下FC
函数式组件,它指导我们如何使用类型。这里我们传入了SeriesProps
props,最后使用了map函数来渲染电视剧列表。
创建表单
现在我们只需创建表单元素,用于提供必要的输入。这里我们将使用受控组件来构建输入元素。为了简单起见,我们将创建一个状态对象,用于保存必要的输入值。我们将使用useState
它来实现这一点。
const [input, setInput] = useState({
name: "",
genre: "",
cover: "",
imdb: 0,
seasons: 0
});
现在我们将渲染组件。这里我们将有五个输入字段,其中三个是文本,两个是数字。
return (
<div className="form-container">
<div className="form-div">
<label htmlFor="name">Name</label>
<input type="text" name="name" id="name" value={input.name} onChange={handleChange} />
</div>
<div className="form-div">
<label htmlFor="genre">Genre</label>
<input type="text" name="genre" id="genre" value={input.genre} onChange={handleChange} />
</div>
<div className="form-div">
<label htmlFor="cover">Cover Link</label>
<input type="text" name="cover" id="cover" value={input.cover} onChange={handleChange} />
</div>
<div className="form-div">
<label htmlFor="imdb">IMDB Rating</label>
<input type="number" name="imdb" id="imdb" value={input.imdb} onChange={handleChange} />
</div>
<div className="form-div">
<label htmlFor="seasons">Total Seasons</label>
<input type="number" name="seasons" id="seasons" value={input.seasons} onChange={handleChange} />
</div>
<button type="button" onClick={handleClick}>Add Series</button>
</div>
);
在这里我们可以看到每个输入字段的值都将存储到状态对象中。我们可以看到所有输入字段都有一个名为的函数,handleChange
该函数将作为侦听器调用onChange
,并且按钮有一个onClick
名为的侦听器handleClick
。我们现在将实现这两种方法。handleChange 方法非常简单。在这里,我们解构状态input
并更新需要更新的特定状态元素。需要注意的一件重要事情是我们传递给该函数的事件类型。这里的类型是,ChangeEvent<HTMLInputElement>
这意味着我们的 handleChange 方法将只接受 html 输入元素更改事件。需要注意的一件事是,我们保留了每个输入的名称和值相同,我们可以使用[name]: value
语句。
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const { value, name } = e.target;
setInput({
...input,
[name]: value
});
};
在实现 handleClick 方法之前,我们需要定义一个 props,用于更新和存储系列列表。由于我们已经在App.tsx
using中定义了一个状态useState
,因此我们需要将其传递到此Form
组件中,并在 handleClick 方法中使用。让我们看看下面的界面。
interface Props {
seriesList: SeriesProps["seriesList"],
setSeriesList: Dispatch<SetStateAction<SeriesProps["seriesList"]>>
}
现在我们将实现我们的handleClick方法。
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
const { name, genre, cover, imdb, seasons } = input;
if(!name && !genre && !cover && !imdb && !seasons) return;
const series = { name, genre, cover, imdb, seasons };
setSeriesList([...seriesList, series]);
setInput({
name: "",
genre: "",
cover: "",
imdb: 0,
seasons: 0
});
};
我们的方法仅接受来自 HTML 按钮元素的鼠标事件。首先,我们解构了输入状态。然后,我们检查是否有任何输入字段为空。如果是,则继续操作。否则,我们创建一个系列对象并将其附加到系列列表中。之后,我们将所有字段清空。因此,我们的完整Form.tsx
代码如下所示:
import React, { FC, useState, ChangeEvent, MouseEvent, Dispatch, SetStateAction } from "react";
import { SeriesProps } from "../interfaces/SeriesProps";
interface Props {
seriesList: SeriesProps["seriesList"],
setSeriesList: Dispatch<SetStateAction<SeriesProps["seriesList"]>>
}
const Form: FC<Props> = ({ seriesList, setSeriesList }) => {
const [input, setInput] = useState({
name: "",
genre: "",
cover: "",
imdb: 0,
seasons: 0
});
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const { value, name } = e.target;
setInput({
...input,
[name]: value
});
};
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
const { name, genre, cover, imdb, seasons } = input;
const series = { name, genre, cover, imdb, seasons };
if(!name && !genre && !cover && !imdb && !seasons) return;
setSeriesList([...seriesList, series]);
setInput({
name: "",
genre: "",
cover: "",
imdb: 0,
seasons: 0
});
};
return (
<div className="form-container">
<div className="form-div">
<label htmlFor="name">Name</label>
<input type="text" name="name" id="name" value={input.name} onChange={handleChange} />
</div>
<div className="form-div">
<label htmlFor="genre">Genre</label>
<input type="text" name="genre" id="genre" value={input.genre} onChange={handleChange} />
</div>
<div className="form-div">
<label htmlFor="cover">Cover Link</label>
<input type="text" name="cover" id="cover" value={input.cover} onChange={handleChange} />
</div>
<div className="form-div">
<label htmlFor="imdb">IMDB Rating</label>
<input type="number" name="imdb" id="imdb" value={input.imdb} onChange={handleChange} />
</div>
<div className="form-div">
<label htmlFor="seasons">Total Seasons</label>
<input type="number" name="seasons" id="seasons" value={input.seasons} onChange={handleChange} />
</div>
<button type="button" onClick={handleClick}>Add Series</button>
</div>
);
};
export default Form;
现在我们只需添加 CSS 样式。为了简单App.css
起见,我们只修改了以下文件:
.form-container {
width: 400px;
margin: auto;
}
h1 {
text-align: center;
}
.form-div {
margin-bottom: 10px;
}
input[type='text'],
input[type='number'] {
float: right;
width: 70%;
padding: 3px;
}
input[type='checkbox'] {
margin-left: 110px;
}
button {
margin: 10px 0;
padding: 10px 0;
width: 100%;
cursor: pointer;
font-weight: bold;
text-transform: uppercase;
font-size: 16px;
}
p {
line-height: 5px;
}
.series-list {
display: flex;
flex-flow: wrap;
margin: 50px auto;
width: 90%;
}
.series-item {
padding: 0 20px 20px 0;
width: 300px;
}
完成所有编码后,我们可以查看浏览器的http://localhost:3000/链接。添加一些数据后,它应该如下所示。整个项目在 GitHub 上。您可以在这里 查看。
祝您编码愉快😀😀😀😀😀
文章来源:https://dev.to/alim1496/react-with-typescript-1gp5