react和nodejs_如何使用React,TypeScript,NodeJS和MongoDB构建Todo应用

 2023-09-06 阅读 26 评论 0

摘要:react和nodejsIn this tutorial, we will be using TypeScript on both sides (server and client) to build a Todo App from scratch with React, NodeJS, Express, and MongoDB. 在本教程中,我们将在双方(服务器和客户端)上使用TypeScript通过React,NodeJS&#

react和nodejs

In this tutorial, we will be using TypeScript on both sides (server and client) to build a Todo App from scratch with React, NodeJS, Express, and MongoDB.

在本教程中,我们将在双方(服务器和客户端)上使用TypeScript通过React,NodeJS,Express和MongoDB从头构建Todo应用程序。

So, let's start by planning the API.

因此,让我们从计划API开始。

  • API with NodeJS, Express, MongoDB and TypeScript

    具有NodeJS,Express,MongoDB和TypeScript的API

  • Setting up

    配置

  • Create a Todo Type

    创建待办事项类型

  • Create a Todo Model

    创建一个待办事项模型

  • Create API controllers

    创建API控制器

  • Get, Add, Update and Delete Todos

    获取,添加,更新和删除待办事项

  • Create API routes

    创建API路由

  • Create a Server

    创建服务器

  • Client-side with React and TypeScript

    带有React和TypeScript的客户端

  • Setting up

    配置

  • Create a Todo Type

    创建待办事项类型

  • Fetch data from the API

    从API提取数据

  • Create the components

    创建组件

  • Add Todo Form

    添加待办事项表格

  • Display a Todo

    显示待办事项

  • Fetch and Display data

    提取并显示数据

  • Resources

    资源资源

Let's dive in.

让我们潜入。

具有NodeJS,Express,MongoDB和TypeScript的API (API with NodeJS, Express, MongoDB and TypeScript)

设定 (Getting set up)

If you're new to this, you can start with A Practical Guide to TypeScript or How to build an API from scratch with Node JS, Express, and MongoDB to get most out of this tutorial. Otherwise, let's get started.

如果您是初学者 ,则可以从TypeScript实用指南或如何使用Node JS,Express和MongoDB从头开始构建API入手,以充分利用本教程。 否则,让我们开始吧。

To create a new NodeJS App, you need to run this command on the terminal:

要创建新的NodeJS App,您需要在终端上运行以下命令:

yarn init

It will ask for a couple of questions and then initialize the app. You can skip it by adding a -y flag to the command.

它将询问几个问题,然后初始化应用程序。 您可以通过在命令中添加-y标志来跳过它。

Next, structure the project as follows:

接下来,按以下方式构建项目:

├── dist
├── node_modules
├── src├── app.ts├── controllers|  └── todos|     └── index.ts├── models|  └── todo.ts├── routes|  └── index.ts└── types└── todo.ts
├── nodemon.json
├── package.json
├── tsconfig.json

As you can see, this file structure is relatively simple. The dist directory will serve as an output folder once the code has compiled to plain JavaScript.

如您所见,此文件结构相对简单。 一旦代码已编译为纯JavaScript, dist目录将用作输出文件夹。

We also have an app.ts file that is the entry point of the server. The controllers, types, and routes are also in their respective folder names.

我们还有一个app.ts文件,它是服务器的入口点。 控制器,类型和路由也位于其各自的文件夹名称中。

Now, we need to configure the tsconfig.json file to help the compiler along following our preferences.

现在,我们需要配置tsconfig.json文件,以帮助编译器遵循我们的首选项。

  • tsconfig.json

    tsconfig.json
{"compilerOptions": {"target": "es6","module": "commonjs","outDir": "dist/js","rootDir": "src","strict": true,"esModuleInterop": true,"forceConsistentCasingInFileNames": true},"include": ["src/**/*"],"exclude": ["src/types/*.ts", "node_modules", ".vscode"]
}

Here we have four main properties to underline:

这里有四个要强调的主要属性:

outDir: tells the compiler to put the compiled code into the dist/js folder.

outDir :告诉编译器将编译后的代码放入dist/js文件夹。

rootDir: informs TypeScript to compile every .ts file located in the src folder.

rootDir :通知TypeScript编译src文件夹中的每个.ts文件。

include: tells the compiler to include files that are in the src directory and sub-directory.

include :告诉编译器包括src目录和子目录中的文件。

exclude: will exclude the files or folders passed in the array during compile-time.

exclude :将在编译时排除在数组中传递的文件或文件夹。

We can now install the dependencies to enable TypeScript in the project. Because by default, this app will use JavaScript.

现在,我们可以安装依赖项以在项目中启用TypeScript。 因为默认情况下,此应用将使用JavaScript。

There are two ways of using TypeScript in a NodeJS app. Either locally in the project or globally in our machine. I will go for the latter based on personal preference, but you can stick with the local way if you want too.

在NodeJS应用程序中有两种使用TypeScript的方法。 在项目本地或我们机器的全球范围内。 我会根据个人喜好选择后者,但是如果您愿意,也可以坚持使用本地方式。

Now, let's execute the following command on the terminal to install TypeScript.

现在,让我们在终端上执行以下命令以安装TypeScript。

yarn add typescript -g

This g flag allows installing TypeScript globally and this makes it accessible from anywhere on the computer.

这个g标志允许全局安装TypeScript,这使得它可以从计算机上的任何位置访问。

Next, let's add some dependencies in order to use Express and MongoDB.

接下来,让我们添加一些依赖项以使用Express和MongoDB。

yarn add express cors mongoose

We also need to install their types as development dependencies to help the TypeScript compiler understand the packages.

我们还需要将它们的类型安装为开发依赖项,以帮助TypeScript编译器理解软件包。

yarn add -D @types/node @types/express @types/mongoose @types/cors

Now, TypeScript won't yell at you anymore - it will use these types to define the libraries we've just installed.

现在,TypeScript不再对您大吼大叫-它会使用这些类型来定义我们刚刚安装的库。

We also need to add other dependencies to be able to compile the TypeScript code and start the server concurrently.

我们还需要添加其他依赖项,以便能够编译TypeScript代码并同时启动服务器。

yarn add -D concurrently nodemon

With that in place, we can now update the package.json file with the scripts needed to start the server.

有了这个,我们现在可以使用启动服务器所需的脚本来更新package.json文件。

  • package.json

    package.json
"scripts": {"build": "tsc","start": "concurrently \"tsc -w\" \"nodemon dist/js/app.js\""}

concurrently will help compile the TypeScript code, keep watching for changes, and also start the server simultaneously. That said, we can now launch the server - however, we have not created something meaningful yet in that regard. So, let's fix that in the next section.

concurrently将有助于编译TypeScript代码,继续监视更改并同时启动服务器。 也就是说,我们现在可以启动服务器了-但是,在这方面我们还没有创建有意义的东西。 因此,让我们在下一部分中进行修复。

创建待办事项类型 (Create a Todo Type)

  • types/todo.ts

    类型/待办事项
import { Document } from "mongoose"export interface ITodo extends Document {name: stringdescription: stringstatus: boolean
}

Here, we have a Todo interface that extends the Document type provided by mongoose. We will be using it later to interact with MongoDB. That said, we can now define how a Todo model should look.

在这里,我们有一个Todo接口,扩展了mongoose提供的Document类型。 我们稍后将使用它与MongoDB进行交互。 也就是说,我们现在可以定义Todo模型的外观。

创建一个待办事项模型 (Create a Todo Model)

  • models/todo.ts

    型号/待办事项
import { ITodo } from "./../types/todo"
import { model, Schema } from "mongoose"const todoSchema: Schema = new Schema({name: {type: String,required: true,},description: {type: String,required: true,},status: {type: Boolean,required: true,},},{ timestamps: true }
)export default model<ITodo>("Todo", todoSchema)

As you can see here, we start by importing the interface ITodo and some utilities from mongoose. The latter helps to define the Todo schema and also pass in ITodo as a type to the model before exporting it.

正如你所看到的,我们通过导入界面开始ITodo和一些公用事业mongoose 。 后者有助于定义Todo模式,并在导出之前将ITodo作为类型传递给model

With that, we can now use the Todo model in other files to interact with the database.

这样,我们现在可以在其他文件中使用Todo模型与数据库进行交互。

创建API控制器 (Create API controllers)

获取,添加,更新和删除待办事项 (Get, Add, Update and Delete Todos)

  • controllers/todos/index.ts

    控制器/待办事项/index.ts
import { Response, Request } from "express"
import { ITodo } from "./../../types/todo"
import Todo from "../../models/todo"const getTodos = async (req: Request, res: Response): Promise<void> => {try {const todos: ITodo[] = await Todo.find()res.status(200).json({ todos })} catch (error) {throw error}
}

Here, we first need to import some types from express because I want to type the values explicitly. If you want, you can let TypeScript infer it for you.

在这里,我们首先需要从express导入一些类型,因为我想显式地键入值。 如果需要,可以让TypeScript为您进行推断。

Next, we use the function getTodos() to fetch data. It receives a req and res parameter and returns a promise.

接下来,我们使用函数getTodos()来获取数据。 它接收一个reqres参数并返回一个Promise。

And with the help of the Todo model created earlier, we can now get data from MongoDB and return a response with the array of todos.

借助之前创建的Todo模型,我们现在可以从MongoDB获取数据并返回包含todos数组的响应。

  • controllers/todos/index.ts

    控制器/待办事项/index.ts
const addTodo = async (req: Request, res: Response): Promise<void> => {try {const body = req.body as Pick<ITodo, "name" | "description" | "status">const todo: ITodo = new Todo({name: body.name,description: body.description,status: body.status,})const newTodo: ITodo = await todo.save()const allTodos: ITodo[] = await Todo.find()res.status(201).json({ message: "Todo added", todo: newTodo, todos: allTodos })} catch (error) {throw error}
}

As you can see, the function addTodo() receives the body object that contains data entered by the user.

如您所见,函数addTodo()接收包含用户输入数据的body对象。

Next, I use typecasting to avoid typos and restrict the body variable to match ITodo and then create a new Todo based on the model.

接下来,我使用类型转换来避免输入错误,并限制body变量以匹配ITodo ,然后基于该模型创建一个新的Todo。

With that in place, we can now save the Todo in the DB and return a response that contains the todo created and the updated todos array.

现在,我们可以将Todo保存在数据库中,并返回一个响应,其中包含创建的todo和更新的todos数组。

  • controllers/todos/index.ts

    控制器/待办事项/index.ts
const updateTodo = async (req: Request, res: Response): Promise<void> => {try {const {params: { id },body,} = reqconst updateTodo: ITodo | null = await Todo.findByIdAndUpdate({ _id: id },body)const allTodos: ITodo[] = await Todo.find()res.status(200).json({message: "Todo updated",todo: updateTodo,todos: allTodos,})} catch (error) {throw error}
}

To update a todo, we need to extract the id and the body from the req object and then pass them to findByIdAndUpdate(). This utility will find the Todo on the database and update it. And once the operation is completed, we can now return the updated data to the user.

要更新待办事项,我们需要从req对象中提取ID和主体,然后将它们传递给findByIdAndUpdate() 。 该实用程序将在数据库上找到待办事项并进行更新。 一旦操作完成,我们现在可以将更新后的数据返回给用户。

  • controllers/todos/index.ts

    控制器/待办事项/index.ts
const deleteTodo = async (req: Request, res: Response): Promise<void> => {try {const deletedTodo: ITodo | null = await Todo.findByIdAndRemove(req.params.id)const allTodos: ITodo[] = await Todo.find()res.status(200).json({message: "Todo deleted",todo: deletedTodo,todos: allTodos,})} catch (error) {throw error}
}export { getTodos, addTodo, updateTodo, deleteTodo }

The function deleteTodo() allows you to delete a Todo from the database. Here, we pull out the id from req and pass it as an argument to findByIdAndRemove() to access the corresponding Todo and delete it from the DB.

函数deleteTodo()允许您从数据库中删除待办事项。 在这里,我们从req中提取id并将其作为参数传递给findByIdAndRemove()以访问相应的Todo并将其从数据库中删除。

Next, we export the functions to be able to use them in other files. That said, we can now create some routes for the API and use these methods to handle the requests.

接下来,我们导出功能以便能够在其他文件中使用它们。 也就是说,我们现在可以为API创建一些路由,并使用这些方法来处理请求。

创建API路由 (Create API routes)

  • routes/index.ts

    路线/index.ts
import { Router } from "express"
import { getTodos, addTodo, updateTodo, deleteTodo } from "../controllers/todos"const router: Router = Router()router.get("/todos", getTodos)router.post("/add-todo", addTodo)router.put("/edit-todo/:id", updateTodo)router.delete("/delete-todo/:id", deleteTodo)export default router

As you can see here, we have four routes to get, add, update, and delete todos from the database. And since we already created the functions, the only thing we have to do is import the methods and pass them as parameters to handle the requests.

如您所见,我们有四种从数据库获取,添加,更新和删除待办事项的途径。 并且由于我们已经创建了函数,所以我们唯一要做的就是导入方法并将它们作为参数传递来处理请求。

So far, we have covered a lot. But we still don't have a server to start. So, let's fix that in the next section.

到目前为止,我们已经介绍了很多内容。 但是我们仍然没有服务器可以启动。 因此,让我们在下一部分中进行修复。

创建服务器 (Create a Server)

Before creating the server, we need to first add some environment variables that will hold the MongoDB credentials in the nodemon.json file.

在创建服务器之前,我们需要首先在nodemon.json文件中添加一些环境变量,这些变量将包含MongoDB凭据。

  • nodemon.json

    nodemon.json
{"env": {"MONGO_USER": "your-username","MONGO_PASSWORD": "your-password","MONGO_DB": "your-db-name"}
}

You can get the credentials by creating a new cluster on MongoDB Atlas.

您可以通过在MongoDB Atlas上创建新集群来获取凭据。

  • app.ts

    应用程序
import express, { Express } from "express"
import mongoose from "mongoose"
import cors from "cors"
import todoRoutes from "./routes"const app: Express = express()const PORT: string | number = process.env.PORT || 4000app.use(cors())
app.use(todoRoutes)const uri: string = `mongodb+srv://${process.env.MONGO_USER}:${process.env.MONGO_PASSWORD}@clustertodo.raz9g.mongodb.net/${process.env.MONGO_DB}?retryWrites=true&w=majority`
const options = { useNewUrlParser: true, useUnifiedTopology: true }
mongoose.set("useFindAndModify", false)mongoose.connect(uri, options).then(() =>app.listen(PORT, () =>console.log(`Server running on http://localhost:${PORT}`))).catch(error => {throw error})

Here, we start by importing the express library that allows us to access the use() method that helps handle the Todos routes.

在这里,我们从导入express库开始,该库允许我们访问有助于处理Todos路线的use()方法。

Next, we use the mongoose package to connect to MongoDB by appending to the URL the credentials held on the nodemon.json file.

接下来,我们使用mongoose包通过将nodemon.json文件中保存的凭据附加到URL来连接到MongoDB。

That said, now if we connect successfully to MongoDB, the server will start. If appropriate, an error will be thrown.

就是说,现在,如果我们成功连接到MongoDB,服务器将启动。 如果合适,将引发错误。

We're now done building the API with Node, Express, TypeScript, and MongoDB. Let's now start building the client-side app with React and TypeScript.

现在,我们已经完成了使用Node,Express,TypeScript和MongoDB构建API的工作。 现在开始使用React和TypeScript构建客户端应用程序。

客户端使用React和TypeScript (Client-side with React and TypeScript)

配置 (Setting up)

To create a new React app, I will go with create-react-app - you can use other methods as well if you want.

要创建一个新的React应用程序,我将使用create-react-app-如果需要,您也可以使用其他方法。

So, let's run in the terminal the following command:

因此,让我们在终端中运行以下命令:

npx create-react-app my-app --template typescript

Next, install the Axios library to be able to fetch remote data.

接下来,安装Axios库以获取远程数据。

yarn add axios

Once the installation completed, let's structure our project as follows:

安装完成后,让我们按以下方式构建项目:

├── node_modules
├── public
├── src
|  ├── API.ts
|  ├── App.test.tsx
|  ├── App.tsx
|  ├── components
|  |  ├── AddTodo.tsx
|  |  └── TodoItem.tsx
|  ├── index.css
|  ├── index.tsx
|  ├── react-app-env.d.ts
|  ├── setupTests.ts
|  └── type.d.ts
├── tsconfig.json
├── package.json
└── yarn.lock

Here, we have a relatively simple file structure. The main thing to notice is that src/type.d.ts will hold the types. And since I will use them on almost every file, I added the extension .d.ts to make the types globally available. And now we don't need to import them anymore.

在这里,我们有一个相对简单的文件结构。 需要注意的主要是src/type.d.ts将保存类型。 而且由于几乎在每个文件上都将使用它们,因此我添加了扩展名.d.ts以使这些类型全局可用。 现在,我们不再需要导入它们。

创建待办事项类型 (Create a Todo Type)

  • src/type.d.ts

    src / type.d.ts
interface ITodo {_id: stringname: stringdescription: stringstatus: booleancreatedAt?: stringupdatedAt?: string
}interface TodoProps {todo: ITodo
}type ApiDataType = {message: stringstatus: stringtodos: ITodo[]todo?: ITodo
}

Here, the ITodo interface needs to mirror the shape of data from the API. And since we don't have mongoose here, we need to add additional properties to match the type defined on the API.

在这里, ITodo接口需要镜像来自API的数据形状。 并且由于这里没有mongoose ,因此我们需要添加其他属性以匹配API上定义的类型。

Next, we use that same interface for the TodoProps which is the type annotation for the props that will be received by the component responsible for rendering the data.

接下来,我们将相同的接口用于TodoProps ,这是负责渲染数据的组件将接收的props的类型注释。

We have now defined our types - let's now start fetching data from the API.

现在,我们已经定义了类型-现在开始从API提取数据。

从API提取数据 (Fetch data from the API)

  • src/API.ts

    src / API.ts
import axios, { AxiosResponse } from "axios"const baseUrl: string = "http://localhost:4000"export const getTodos = async (): Promise<AxiosResponse<ApiDataType>> => {try {const todos: AxiosResponse<ApiDataType> = await axios.get(baseUrl + "/todos")return todos} catch (error) {throw new Error(error)}
}

As you can see, we need to import axios to request data from the API. Next, we use the function getTodos() to get data from the server. It will return a promise of type AxiosResponse that holds the Todos fetched that need to match the type ApiDataType.

如您所见,我们需要导入axios以从API请求数据。 接下来,我们使用函数getTodos()从服务器获取数据。 它将返回AxiosResponse类型的AxiosResponse ,该AxiosResponse包含已获取的Todos,需要与ApiDataType类型进行匹配。

  • src/API.ts

    src / API.ts
export const addTodo = async (formData: ITodo
): Promise<AxiosResponse<ApiDataType>> => {try {const todo: Omit<ITodo, "_id"> = {name: formData.name,description: formData.description,status: false,}const saveTodo: AxiosResponse<ApiDataType> = await axios.post(baseUrl + "/add-todo",todo)return saveTodo} catch (error) {throw new Error(error)}
}

This function receives the data entered by the user as an argument and returns a promise. Here, we need to omit the _id property because MongoDB will create it on the fly.

该函数接收用户输入的数据作为参数并返回promise。 在这里,我们需要省略_id属性,因为MongoDB会即时创建它。

  • src/API.ts

    src / API.ts
export const updateTodo = async (todo: ITodo
): Promise<AxiosResponse<ApiDataType>> => {try {const todoUpdate: Pick<ITodo, "status"> = {status: true,}const updatedTodo: AxiosResponse<ApiDataType> = await axios.put(`${baseUrl}/edit-todo/${todo._id}`,todoUpdate)return updatedTodo} catch (error) {throw new Error(error)}
}

To update a Todo, we have to pass in the updated data and the _id of the object. Here, we need to change the status of the Todo, which is why I only pick the property we need before sending the request to the server.

要更新Todo,我们必须传入更新后的数据和对象的_id 。 在这里,我们需要更改Todo的status ,这就是为什么我只在将请求发送到服务器之前选择我们需要的属性的原因。

  • src/API.ts

    src / API.ts
export const deleteTodo = async (_id: string
): Promise<AxiosResponse<ApiDataType>> => {try {const deletedTodo: AxiosResponse<ApiDataType> = await axios.delete(`${baseUrl}/delete-todo/${_id}`)return deletedTodo} catch (error) {throw new Error(error)}
}

Here, we also have a function that receives as a parameter the _id property and returns a promise.

在这里,我们还有一个函数接收_id属性作为参数并返回一个Promise。

With that in place, we can now go to the components folder and add some meaningful code to its files.

有了这个,我们现在可以转到components文件夹并将一些有意义的代码添加到其文件中。

创建组件 (Create the components)

添加待办事项表格 (Add Todo Form)

  • components/AddTodo.tsx

    组件/AddTodo.tsx
import React from "react"type Props = TodoProps & {updateTodo: (todo: ITodo) => voiddeleteTodo: (_id: string) => void
}const Todo: React.FC<Props> = ({ todo, updateTodo, deleteTodo }) => {const checkTodo: string = todo.status ? `line-through` : ""return (<div className="Card"><div className="Card--text"><h1 className={checkTodo}>{todo.name}</h1><span className={checkTodo}>{todo.description}</span></div><div className="Card--button"><buttononClick={() => updateTodo(todo)}className={todo.status ? `hide-button` : "Card--button__done"}>Complete</button><buttononClick={() => deleteTodo(todo._id)}className="Card--button__delete">Delete</button></div></div>)
}export default Todo

As you can see, here we have a functional component of type React.FC (FC stands for functional component). It receives as a prop the method saveTodo() that allows us to save data to the DB.

如您所见,这里我们有一个React.FC类型的功能组件(FC代表功能组件)。 它接收作为方法的saveTodo()方法,该方法使我们可以将数据保存到DB。

Next, we have a formData state that needs to match the ITodo type to satisfy the compiler. That is why we pass it to the useState hook. We also need to add an alternative type ({}) because the initial state will be an empty object.

接下来,我们有一个formData状态,该状态需要匹配ITodo类型才能满足编译器的要求。 这就是为什么我们将其传递给useState挂钩。 我们还需要添加替代类型( {} ),因为初始状态将是一个空对象。

And with that, we can now move forward and display the data fetched.

这样,我们现在就可以前进并显示获取的数据。

显示待办事项 (Display a Todo)

  • components/TodoItem.tsx

    组件/TodoItem.tsx
import React from "react"type Props = TodoProps & {updateTodo: (todo: ITodo) => voiddeleteTodo: (_id: string) => void
}const Todo: React.FC<Props> = ({ todo, updateTodo, deleteTodo }) => {const checkTodo: string = todo.status ? `line-through` : ""return (<div className="Card"><div className="Card--text"><h1 className={checkTodo}>{todo.name}</h1><span className={checkTodo}>{todo.description}</span></div><div className="Card--button"><buttononClick={() => updateTodo(todo)}className={todo.status ? `hide-button` : "Card--button__done"}>Complete</button><buttononClick={() => deleteTodo(todo._id)}className="Card--button__delete">Delete</button></div></div>)
}export default Todo

Here, we need to extend the TodoProps type and append the functions updateTodo and deleteTodo to handle appropriately the props received by the component.

在这里,我们需要扩展TodoProps类型,并附加函数updateTododeleteTodo以适当地处理组件接收到的props。

Now, once the Todo object passed in, we will be able to display it and add the functions needed to update or delete a Todo.

现在,一旦Todo对象传入,我们将能够显示它并添加更新或删除Todo所需的功能。

Great! We can now go to the App.tsx file and add the last piece to the puzzle.

大! 现在,我们可以转到App.tsx文件,并将最后一块添加到拼图中。

提取并显示数据 (Fetch and Display data)

  • App.tsx

    App.tsx
import React, { useEffect, useState } from 'react'
import TodoItem from './components/TodoItem'
import AddTodo from './components/AddTodo'
import { getTodos, addTodo, updateTodo, deleteTodo } from './API'const App: React.FC = () => {const [todos, setTodos] = useState<ITodo[]>([])useEffect(() => {fetchTodos()}, [])const fetchTodos = (): void => {getTodos().then(({ data: { todos } }: ITodo[] | any) => setTodos(todos)).catch((err: Error) => console.log(err))}

Here, we first need to import the components and utility functions held on API.ts. Next, we pass to useState an array of type ITodo and initialize it with an empty array.

在这里,我们首先需要导入API.tsAPI.ts的组件和实用程序功能。 接着,我们通过useState类型的数组ITodo并用一个空数组初始化它。

The method getTodos() returns a promise - therefore, we can access the then function and update the state with the data fetched or throw an error if any occurs.

方法getTodos()返回一个getTodos()因此,我们可以访问then函数并使用获取的数据更新状态,或者在发生任何错误时引发错误。

With that in place, we can now call the function fetchTodos() when the component is successfully mounted.

有了这个,我们现在可以在成功安装组件后调用函数fetchTodos()

  • App.tsx

    App.tsx
const handleSaveTodo = (e: React.FormEvent, formData: ITodo): void => {e.preventDefault()addTodo(formData).then(({ status, data }) => {if (status !== 201) {throw new Error("Error! Todo not saved")}setTodos(data.todos)}).catch(err => console.log(err))
}

Once the form is submitted, we use addTodo() to send the request to the server, and then if the Todo has successfully saved, we update the data, otherwise an error will be thrown.

提交表单后,我们使用addTodo()将请求发送到服务器,然后如果Todo成功保存,我们将更新数据,否则将引发错误。

  • App.tsx

    App.tsx
const handleUpdateTodo = (todo: ITodo): void => {updateTodo(todo).then(({ status, data }) => {if (status !== 200) {throw new Error("Error! Todo not updated")}setTodos(data.todos)}).catch(err => console.log(err))
}const handleDeleteTodo = (_id: string): void => {deleteTodo(_id).then(({ status, data }) => {if (status !== 200) {throw new Error("Error! Todo not deleted")}setTodos(data.todos)}).catch(err => console.log(err))
}

The functions to update or delete a Todo are quite similar. They both receive a parameter, send the request, and get back a response. And then, they check if the request has been successful and handle it accordingly.

更新或删除待办事项的功能非常相似。 它们都接收参数,发送请求,并返回响应。 然后,他们检查请求是否成功,并进行相应处理。

  • App.tsx

    App.tsx
return (<main className='App'><h1>My Todos</h1><AddTodo saveTodo={handleSaveTodo} />{todos.map((todo: ITodo) => (<TodoItemkey={todo._id}updateTodo={handleUpdateTodo}deleteTodo={handleDeleteTodo}todo={todo}/>))}</main>)
}export default App

Here, we loop through the todos array and then pass to the TodoItem the expected data.

在这里,我们遍历todos数组,然后将期望的数据传递给TodoItem

Now, if you browse on the folder that contains the server-side app (and execute the following command in the terminal):

现在,如果您浏览包含服务器端应用程序的文件夹(并在终端中执行以下命令):

yarn start

And also on the client-side app:

以及在客户端应用程序上:

yarn start

You should see that our Todo app works as expected.

您应该看到我们的Todo应用程序可以正常工作。

Great! With that final touch, we have now finished building a Todo App using TypeScript, React, NodeJs, Express, and MongoDB.

大! 通过最后的接触,我们现在完成了使用TypeScript,React,NodeJs,Express和MongoDB构建Todo应用程序的工作。

You can find the Source Code here.

您可以在此处找到源代码 。

You can find other great content like this on my blog or follow me on Twitter to get notified.

您可以在我的博客上找到类似的其他精彩内容,或者在Twitter上关注我以获取通知。

Thanks for reading.

谢谢阅读。

资源资源 (Resources)

React TypeScript Cheatsheet

ReactTypeScript备忘单

Advanced TypeScript Types cheatsheet (with examples)

高级TypeScript类型速查表(包含示例)

TypeScript Cheatsheets

TypeScript备忘单

翻译自: https://www.freecodecamp.org/news/how-to-build-a-todo-app-with-react-typescript-nodejs-and-mongodb/

react和nodejs

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/2/7037.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息