如何使用React和TypeScript构建客户列表管理应用

The author selected the Tech Education Fund to receive a donation as part of the Write for DOnations program.

作者选择了Tech Education Fund作为“ Write for DOnations”计划的一部分来接受捐赠。

介绍 (Introduction)

TypeScript has brought a lot of improvement into how JavaScript developers structure and write code for apps, especially web applications. Defined as a superset of JavaScript, TypeScript behaves identically to JavaScript but with extra features designed to help developers build larger and more complex programs with fewer or no bugs. TypeScript is increasingly gaining popularity; adopted by major companies like Google for the Angular web framework. The Nest.js back-end framework was also built with TypeScript.

TypeScriptJavaScript开发人员如何构造和编写应用程序(尤其是Web应用程序)的代码方面带来了很多改进。 TypeScript被定义为JavaScript的超集,其行为与JavaScript相同,但具有额外的功能,旨在帮助开发人员构建更少,没有错误的大型,复杂程序。 TypeScript越来越受欢迎。 Google等主要公司采用Angular Web框架。 Nest.js后端框架也是使用TypeScript构建的。

One of the ways to improve productivity as a developer is the ability to implement new features as quickly as possible without any concern over breaking the existing app in production. To achieve this, writing statically typed code is a style adopted by many seasoned developers. Statically typed programming languages like TypeScript enforce an association for every variable with a data type; such as a string, integer, boolean, and so on. One of the major benefits of using a statically typed programming language is that type checking is completed at compile time, therefore developers can see errors in their code at a very early stage.

作为开发人员提高生产率的方法之一是能够尽快实施新功能,而不必担心会破坏生产中的现有应用程序。 为此,编写静态类型的代码是许多经验丰富的开发人员所采用的样式。 诸如TypeScript之类的静态类型编程语言会强制将每个变量与数据类型相关联。 例如字符串,整数,布尔值等。 使用静态类型的编程语言的主要好处之一是类型检查在编译时完成,因此开发人员可以在很早的阶段看到其代码中的错误。

React is an open-source JavaScript library, which developers use to create high-end user interfaces for scalable web applications. The great performance and dynamic user interfaces built with React for single-page applications make it a popular choice among developers.

React是一个开放源代码JavaScript库,开发人员可以使用它来为可伸缩Web应用程序创建高端用户界面。 React为单页面应用程序构建的出色性能和动态用户界面使其成为开发人员中的流行选择。

In this tutorial, you will create a customer list management application with a separate REST API backend and a frontend built with React and TypeScript. You will build the backend using a fake REST API named json-server. You’ll use it to quickly set up a CRUD (Create, Read, Update, and Delete) backend. Consequently you can focus on handling the front-end logic of an application using React and TypeScript.

在本教程中,您将创建一个客户列表管理应用程序,该应用程序具有一个单独的REST API后端以及使用React和TypeScript构建的前端。 您将使用名为json-server的伪造REST API构建后端。 您将使用它来快速设置CRUD(创建,读取,更新和删除)后端。 因此,您可以专注于使用React和TypeScript处理应用程序的前端逻辑。

先决条件 (Prerequisites)

To complete this tutorial, you will need:

要完成本教程,您将需要:

第1步—安装TypeScript和创建React应用程序 (Step 1 — Installing TypeScript and Creating the React Application)

In this step, you will install the TypeScript package globally on your machine by using the Node Package Manager (npm). After that, you will also install React and its dependencies, and check that your React app is working by running the development server.

在此步骤中,您将使用节点包管理器( npm )在您的计算机上全局安装TypeScript包。 之后,您还将安装React及其依赖项,并通过运行开发服务器来检查React应用是否正常运行。

To begin, open a terminal and run the following command to install TypeScript:

首先,打开终端并运行以下命令来安装TypeScript:

  • npm install -g typescript

    npm install -g打字稿

Once the installation process is complete, execute the following command to check your installation of TypeScript:

安装过程完成后,执行以下命令来检查TypeScript的安装:

  • tsc -v

    tsc -v

You will see the current version installed on your machine:

您将看到计算机上安装的当前版本:


   
   
Output
Version 3.4.5

Next, you will install the React application by using the create-react-app tool to set up the application with a single command. You’ll use the npx command, which is a package runner tool that comes with npm 5.2+. The create-react-app tool has built-in support for working with TypeScript without any extra configuration required. Run the following command to create and install a new React application named typescript-react-app:

接下来,您将使用create-react-app工具安装React应用程序,以通过单个命令设置应用程序。 您将使用npx命令,这是npm 5.2+附带的打包npm工具。 create-react-app工具内置支持使用TypeScript,而无需任何额外配置。 运行以下命令以创建并安装一个名为typescript-react-app的新React应用typescript-react-app

  • npx create-react-app typescript-react-app --typescript

    npx create -react-app typescript -react-app --typescript

The preceding command will create a new React application with the name typescript-react-app. The --typescript flag will set the default filetype for React components to .tsx.

前面的命令将创建一个名为typescript-react-app的新React应用typescript-react-app--typescript标志会将React组件的默认文件类型设置为.tsx

Before you complete this section, the application will require moving from one port to another. To do that, you will need to install a routing library for your React application named React Router and its corresponding TypeScript definitions. You will use yarn to install the library and other packages for this project. This is because yarn is faster, especially for installing dependencies for a React application. Move into the newly created project folder and then install React Router with the following command:

在完成本节之前,应用程序将需要从一个端口移至另一个端口。 为此,您将需要为名为React Router的 React应用程序及其相应的TypeScript定义安装路由库。 您将使用yarn安装该项目的库和其他软件包。 这是因为yarn更快,尤其是对于安装React应用程序的依赖项。 移至新创建的项目文件夹,然后使用以下命令安装React Router:

  • cd typescript-react-app

    光盘打字稿React应用程序

  • yarn add react-router-dom

    纱线添加React路由器

You now have the React Router package, which will provide the routing functionality within your project. Next, run the following command to install the TypeScript definitions for React Router:

现在,您有了React Router软件包,它将在您的项目中提供路由功能。 接下来,运行以下命令为React Router安装TypeScript定义:

  • yarn add @types/react-router-dom

    纱线添加@ types / react-router-dom

Now you’ll install axios, which is a promised-based HTTP client for browsers, to help with the process of performing HTTP requests from the different components that you will create within the application:

现在,您将安装axios (这是基于浏览器的基于HTTP的HTTP客户端),以帮助执行将在应用程序中创建的不同组件执行HTTP请求的过程:

  • yarn add axios

    纱线添加轴

Once the installation process is complete, start the development server with:

安装过程完成后,使用以下命令启动开发服务器:

  • yarn start

    纱线开始

Your application will be running on http://localhost:3000.

您的应用程序将在http://localhost:3000

You have successfully installed TypeScript, created a new React application, and installed React Router in order to help with navigating from one page of the application to another. In the next section, you will set up the back-end server for the application.

您已成功安装TypeScript,创建了新的React应用程序并安装了React Router,以帮助从应用程序的一个页面导航到另一页面。 在下一部分中,您将为应用程序设置后端服务器。

第2步-创建JSON服务器 (Step 2 — Creating a JSON Server)

In this step, you’ll create a mock server that your React application can quickly connect with, as well as use its resources. It is important to note that this back-end service is not suitable for an application in production. You can use Nest.js, Express, or any other back-end technology to build a RESTful API in production. json-server is a useful tool whenever you need to create a prototype and mock a back-end server.

在此步骤中,您将创建一个模拟服务器,您的React应用程序可以快速连接该模拟服务器,并使用其资源。 重要的是要注意,此后端服务不适用于生产中的应用程序。 您可以使用Nest.js,Express或任何其他后端技术在生产环境中构建RESTful API。 每当需要创建原型并模拟后端服务器时, json-server都是有用的工具。

You can use either npm or yarn to install json-server on your machine. This will make it available from any directory of your project whenever you might need to make use of it. Open a new terminal window and run this command to install json-server while you are still within the project directory:

您可以使用npmyarn来在机器上安装json-server 。 每当您可能需要使用它时,它都可以从项目的任何目录中使用。 当您仍在项目目录中时,打开一个新的终端窗口并运行以下命令以安装json-server

  • yarn global add json-server

    毛线全局添加json服务器

Next, you will create a JSON file that will contain the data that will be exposed by the REST API. For the objects specified in this file (which you’ll create), a CRUD endpoint will be generated automatically. To begin, create a new folder named server and then move into it:

接下来,您将创建一个JSON文件,其中将包含REST API将公开的数据。 对于此文件(您将创建)中指定的对象,将自动生成一个CRUD端点。 首先,创建一个名为server的新文件夹,然后移至其中:

  • mkdir server

    mkdir服务器
  • cd server

    CD服务器

Now, use nano to create and open a new file named db.json:

现在,使用nano创建并打开一个名为db.json的新文件:

  • nano db.json

    纳米db.json

Add the following content to the file:

将以下内容添加到文件中:

/server/db.json
/服务器/db.json
{
    "customers": [
        {
            "id": 1,
            "first_name": "Customer_1",
            "last_name": "Customer_11",
            "email": "customer1@mail.com",
            "phone": "00000000000",
            "address": "Customer_1 Address",
            "description": "Customer_1 description"
        },
        {
            "id": 2,
            "first_name": "Customer_2",
            "last_name": "Customer_2",
            "email": "customer2@mail.com",
            "phone": "00000000000",
            "address": "Customer_2 Adress",
            "description": "Customer_2 Description"
        }
    ]
}

The JSON structure consists of a customer object, which has two datasets assigned. Each customer consists of seven properties: id, description, first_name, last_name, email, phone, and address.

JSON结构由一个客户对象组成,该客户对象分配了两个数据集。 每个客户都包含七个属性: iddescriptionfirst_namelast_nameemailphoneaddress

Save and exit the file.

保存并退出文件。

By default, the json-server runs on port 3000—this is the same port on which your React application runs. To avoid conflict, you can change the default port for the json-server. To do that, move to the root directory of the application:

默认情况下, json-server在端口3000上运行-这与您的React应用程序在同一端口上运行。 为了避免冲突,您可以更改json-server的默认端口。 为此,请移至应用程序的根目录:

  • cd ~/typescript-react-app

    cd〜/ typescript-react-app

Open the application with your preferred text editor and create a new file named json-server.json:

使用首选的文本编辑器打开应用程序,然后创建一个名为json-server.json的新文件:

  • nano json-server.json

    纳米json-server.json

Now insert the following to update the port number:

现在插入以下内容以更新端口号:

/json-server.json
/json-server.json
{
    "port": 5000
}

This will act as the configuration file for the json-server and it will ensure that the server runs on the port specified in it at all times.

这将充当json-server的配置文件,并确保服务器始终在其中指定的端口上运行。

Save and exit the file.

保存并退出文件。

To run the server, use the following command:

要运行服务器,请使用以下命令:

  • json-server --watch server/db.json

    json-server --watch server / db.json

This will start the json-server on port 5000. If you navigate to http://localhost:5000/customers in your browser, you will see the server showing your customer list.

这将在端口5000上启动json-server 。 如果在浏览器中导航到http://localhost:5000/customers ,您将看到服务器显示您的客户列表。

To streamline the process of running the json-server, you can update package.json with a new property named server to the scripts object as shown here:

为了简化json-server的运行过程,您可以使用名为server的新属性将package.json更新到scripts对象,如下所示:

/package.json
/package.json
{
...
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "server": "json-server --watch server/db.json"
  },
...
}

Save and exit the file.

保存并退出文件。

Now anytime you wish to start the json-server, all you have to do is run yarn server from the terminal.

现在,只要您想启动json-server ,您所要做的就是从终端运行yarn server

You’ve created a simple REST API that you will use as the back-end server for this application. You also created a customer JSON object that will be used as the default data for the REST API. Lastly, you configured an alternative port for the back-end server powered by json-server. Next, you will build reusable components for your application.

您已经创建了一个简单的REST API,它将用作此应用程序的后端服务器。 您还创建了一个客户JSON对象,该对象将用作REST API的默认数据。 最后,您为由json-server驱动的后端服务器配置了备用端口。 接下来,您将为应用程序构建可重用的组件。

步骤3 —创建可重用组件 (Step 3 — Creating Reusable Components)

In this section, you will create the required React components for the application. This will include components to create, display, and edit the details of a particular customer in the database respectively. You’ll also build some of the TypeScript interfaces for your application.

在本部分中,您将为应用程序创建所需的React组件。 这将包括分别在数据库中创建,显示和编辑特定客户详细信息的组件。 您还将为您的应用程序构建一些TypeScript接口。

To begin, move back to the terminal where you have the React application running and stop the development server with CTRL + C. Next, navigate to the ./src/ folder:

首先,回到运行React应用程序的终端,然后使用CTRL + C停止开发服务器。 接下来,导航到./src/文件夹:

  • cd ./src/

    cd ./src/

Then, create a new folder named components inside of it and move into the new folder:

然后,在其中创建一个名为components的新文件夹,并移至新文件夹:

  • mkdir components

    mkdir组件
  • cd components

    cd组件

Within the newly created folder, create a customer folder and then move into it:

在新创建的文件夹中,创建一个customer文件夹,然后移到其中:

  • mkdir customer

    mkdir客户
  • cd customer

    cd客户

Now create two new files named Create.tsx and Edit.tsx:

现在创建两个名为Create.tsxEdit.tsx新文件:

  • touch Create.tsx Edit.tsx

    触摸Create.tsx Edit.tsx

These files are React reusable components that will render the forms and hold all the business logic for creating and editing the details of a customer respectively.

这些文件是React可重用的组件,这些组件将呈现表单并保存用于分别创建和编辑客户详细信息的所有业务逻辑。

Open the Create.tsx file in your text editor and add the following code:

在文本编辑器中打开Create.tsx文件,并添加以下代码:

/src/components/customer/Create.tsx
/src/components/customer/Create.tsx
import * as React from 'react';
import axios from 'axios';
import { RouteComponentProps, withRouter } from 'react-router-dom';

export interface IValues {
    first_name: string,
    last_name: string,
    email: string,
    phone: string,
    address: string,
    description: string,
}
export interface IFormState {
    [key: string]: any;
    values: IValues[];
    submitSuccess: boolean;
    loading: boolean;
}

Here you’ve imported React, axios, and other required components necessary for routing from the React Router package. After that you created two new interfaces named IValues and IFormState. TypeScript interfaces help to define the specific type of values that should be passed to an object and enforce consistency throughout an application. This ensures that bugs are less likely to appear in your program.

在这里,您已经从React Router包中导入了Reactaxios和其他必需的组件,这些组件是路由所需的。 之后,您创建了两个名为IValuesIFormState新接口。 TypeScript 接口有助于定义应传递给对象的值的特定类型,并在整个应用程序中实现一致性。 这样可以确保错误很少出现在您的程序中。

Next, you will build a Create component that extends React.Component. Add the following code to the Create.tsx file immediately after the IFormState interface:

接下来,您将构建一个扩展React.ComponentCreate组件。 在IFormState接口之后,立即将以下代码添加到Create.tsx文件中:

/src/components/customer/Create.tsx
/src/components/customer/Create.tsx
...
class Create extends React.Component<RouteComponentProps, IFormState> {
    constructor(props: RouteComponentProps) {
        super(props);
        this.state = {
            first_name: '',
            last_name: '',
            email: '',
            phone: '',
            address: '',
            description: '',
            values: [],
            loading: false,
            submitSuccess: false,
        }
    }
}
export default withRouter(Create)

Here you’ve defined a React component in Typescript. In this case, the Create class component accepts props (short for “properties”) of type RouteComponentProps and uses a state of type IFormState. Then, inside the constructor, you initialized the state object and defined all the variables that will represent the rendered values for a customer.

在这里,您已经在Typescript中定义了一个React组件。 在这种情况下, Create类组件接受props (短为“属性”)类型的RouteComponentProps和使用类型的状态IFormState 。 然后,在构造函数内部,初始化state对象,并定义所有代表客户呈现值的变量。

Next, add these methods within the Create class component, just after the constructor. You’ll use these methods to process customer forms and handle all changes in the input fields:

接下来,将这些方法添加到Create类组件中,紧随构造函数之后。 您将使用以下方法来处理客户表单并处理输入字段中的所有更改:

/src/components/customer/Create.tsx
/src/components/customer/Create.tsx
...
          values: [],
          loading: false,
          submitSuccess: false,
      }
  }

  private processFormSubmission = (e: React.FormEvent<HTMLFormElement>): void => {
          e.preventDefault();
          this.setState({ loading: true });
          const formData = {
              first_name: this.state.first_name,
              last_name: this.state.last_name,
              email: this.state.email,
              phone: this.state.phone,
              address: this.state.address,
              description: this.state.description,
          }
          this.setState({ submitSuccess: true, values: [...this.state.values, formData], loading: false });
          axios.post(`http://localhost:5000/customers`, formData).then(data => [
              setTimeout(() => {
                  this.props.history.push('/');
              }, 1500)
          ]);
      }

      private handleInputChanges = (e: React.FormEvent<HTMLInputElement>) => {
          e.preventDefault();
          this.setState({
              [e.currentTarget.name]: e.currentTarget.value,
      })
  }

...
export default withRouter(Create)
...

The processFormSubmission() method receives the details of the customer from the application state and posts it to the database using axios. The handleInputChanges() uses React.FormEvent to obtain the values of all input fields and calls this.setState() to update the state of the application.

processFormSubmission()方法从应用程序状态接收客户的详细信息,然后使用axios将其发布到数据库中。 handleInputChanges()使用React.FormEvent获取所有输入字段的值,并调用this.setState()来更新应用程序的状态。

Next, add the render() method within the Create class component immediately after the handleInputchanges() method. This render() method will display the form to create a new customer in the application:

接下来,在handleInputchanges()方法之后立即在Create类组件中添加render() handleInputchanges()方法。 此render()方法将显示表单以在应用程序中创建新客户:

/src/components/customer/Create.tsx
/src/components/customer/Create.tsx
...
  public render() {
      const { submitSuccess, loading } = this.state;
      return (
          <div>
              <div className={"col-md-12 form-wrapper"}>
                  <h2> Create Post </h2>
                  {!submitSuccess && (
                      <div className="alert alert-info" role="alert">
                          Fill the form below to create a new post
                  </div>
                  )}
                  {submitSuccess && (
                      <div className="alert alert-info" role="alert">
                          The form was successfully submitted!
                          </div>
                  )}
                  <form id={"create-post-form"} onSubmit={this.processFormSubmission} noValidate={true}>
                      <div className="form-group col-md-12">
                          <label htmlFor="first_name"> First Name </label>
                          <input type="text" id="first_name" onChange={(e) => this.handleInputChanges(e)} name="first_name" className="form-control" placeholder="Enter customer's first name" />
                      </div>
                      <div className="form-group col-md-12">
                          <label htmlFor="last_name"> Last Name </label>
                          <input type="text" id="last_name" onChange={(e) => this.handleInputChanges(e)} name="last_name" className="form-control" placeholder="Enter customer's last name" />
                      </div>
                      <div className="form-group col-md-12">
                          <label htmlFor="email"> Email </label>
                          <input type="email" id="email" onChange={(e) => this.handleInputChanges(e)} name="email" className="form-control" placeholder="Enter customer's email address" />
                      </div>
                      <div className="form-group col-md-12">
                          <label htmlFor="phone"> Phone </label>
                          <input type="text" id="phone" onChange={(e) => this.handleInputChanges(e)} name="phone" className="form-control" placeholder="Enter customer's phone number" />
                      </div>
                      <div className="form-group col-md-12">
                          <label htmlFor="address"> Address </label>
                          <input type="text" id="address" onChange={(e) => this.handleInputChanges(e)} name="address" className="form-control" placeholder="Enter customer's address" />
                      </div>
                      <div className="form-group col-md-12">
                          <label htmlFor="description"> Description </label>
                          <input type="text" id="description" onChange={(e) => this.handleInputChanges(e)} name="description" className="form-control" placeholder="Enter Description" />
                      </div>
                      <div className="form-group col-md-4 pull-right">
                          <button className="btn btn-success" type="submit">
                              Create Customer
                          </button>
                          {loading &&
                              <span className="fa fa-circle-o-notch fa-spin" />
                          }
                      </div>
                  </form>
              </div>
          </div>
      )
  }
...

Here, you created a form with the input fields to hold the values of the first_name, last_name, email, phone, address, and description of a customer. Each of the input fields have a method handleInputChanges() that runs on every keystroke, updating the React state with the value it obtains from the input field. Furthermore, depending on the state of the application, a boolean variable named submitSuccess will control the message that the application will display before and after creating a new customer.

在这里,您创建了一个带有输入字段的表单,用于保存first_namelast_nameemailphoneaddress和客户description的值。 每个输入字段都有一个方法handleInputChanges() ,该方法在每次击键时运行,并使用从输入字段获得的值更新React state 。 此外,根据应用程序的状态,一个名为submitSuccess的布尔变量将控制在创建新客户之前和之后应用程序将显示的消息。

You can see the complete code for this file in this GitHub repository.

您可以在GitHub存储库中看到此文件的完整代码。

Save and exit Create.tsx.

保存并退出Create.tsx

Now that you have added the appropriate logic to the Create component file for the application, you’ll proceed to add contents for the Edit component file.

现在,您已经为应用程序的“ Create组件文件添加了适当的逻辑,接下来将为“ Edit组件文件添加内容。

Open your Edit.tsx file within the customer folder, and start by adding the following content to import React, axios, and also define TypeScript interfaces:

customer文件夹中打开您的Edit.tsx文件,首先添加以下内容以导入Reactaxios并定义TypeScript接口:

/src/components/customer/Edit.tsx
/src/components/customer/Edit.tsx
import * as React from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import axios from 'axios';

export interface IValues {
    [key: string]: any;
}
export interface IFormState {
    id: number,
    customer: any;
    values: IValues[];
    submitSuccess: boolean;
    loading: boolean;
}

Similarly to the Create component, you import the required modules and create IValues and IFormState interfaces respectively. The IValues interface defines the data type for the input fields’ values, while you’ll use IFormState to declare the expected type for the state object of the application.

Create组件类似,您将导入所需的模块并分别创建IValuesIFormState接口。 IValues接口定义输入字段值的数据类型,而您将使用IFormState声明应用程序状态对象的预期类型。

Next, create the EditCustomer class component directly after the IFormState interface block as shown here:

接下来,在IFormState接口块之后直接创建EditCustomer类组件,如下所示:

/src/components/customer/Edit.tsx
/src/components/customer/Edit.tsx
...
class EditCustomer extends React.Component<RouteComponentProps<any>, IFormState> {
    constructor(props: RouteComponentProps) {
        super(props);
        this.state = {
            id: this.props.match.params.id,
            customer: {},
            values: [],
            loading: false,
            submitSuccess: false,
        }
    }
}
export default withRouter(EditCustomer)

This component takes the RouteComponentProps<any> and an interface of IFormState as a parameter. You use the addition of <any> to the RouteComponentProps because whenever React Router parses path parameters, it doesn’t do any type conversion to ascertain whether the type of the data is number or string. Since you’re expecting a parameter for uniqueId of a customer, it is safer to use any.

该组件将RouteComponentProps<any>IFormState的接口作为参数。 您将<any>RouteComponentProps是因为每当React Router解析路径参数时,它都不会进行任何类型转换来确定数据的类型是number还是string 。 由于您期望客户的uniqueId参数,因此使用any uniqueId安全。

Now add the following methods within the component:

现在,在组件中添加以下方法:

/src/components/customer/Edit.tsx
/src/components/customer/Edit.tsx
...
    public componentDidMount(): void {
        axios.get(`http://localhost:5000/customers/${this.state.id}`).then(data => {
            this.setState({ customer: data.data });
        })
    }

    private processFormSubmission = async (e: React.FormEvent<HTMLFormElement>): Promise<void> => {
        e.preventDefault();
        this.setState({ loading: true });
        axios.patch(`http://localhost:5000/customers/${this.state.id}`, this.state.values).then(data => {
            this.setState({ submitSuccess: true, loading: false })
            setTimeout(() => {
                this.props.history.push('/');
            }, 1500)
        })
    }

    private setValues = (values: IValues) => {
        this.setState({ values: { ...this.state.values, ...values } });
    }
    private handleInputChanges = (e: React.FormEvent<HTMLInputElement>) => {
        e.preventDefault();
        this.setValues({ [e.currentTarget.id]: e.currentTarget.value })
    }
...
}

export default withRouter(EditCustomer)

First, you add a componentDidMount() method, which is a lifecycle method that is being called when the component is created. The method takes the id obtained from the route parameter to identify a particular customer as a parameter, uses it to retrieve their details from the database and then populates the form with it. Furthermore, you add methods to process form submission and handle changes made to the values of the input fields.

首先,添加一个componentDidMount()方法,这是一个生命周期方法,在创建组件时会调用该方法。 该方法采用从route参数获得的id来识别特定客户作为参数,使用它从数据库中检索他们的详细信息,然后用它填充表单。 此外,您添加方法来处理表单提交并处理对输入字段的值所做的更改。

Lastly, add the render() method for the Edit component:

最后,为Edit组件添加render()方法:

/src/components/customer/Edit.tsx
/src/components/customer/Edit.tsx
...
    public render() {
        const { submitSuccess, loading } = this.state;
        return (
            <div className="App">
                {this.state.customer &&
                    <div>
                        < h1 > Customer List Management App</h1>
                        <p> Built with React.js and TypeScript </p>

                        <div>
                            <div className={"col-md-12 form-wrapper"}>
                                <h2> Edit Customer </h2>
                                {submitSuccess && (
                                    <div className="alert alert-info" role="alert">
                                        Customer's details has been edited successfully </div>
                                )}
                                <form id={"create-post-form"} onSubmit={this.processFormSubmission} noValidate={true}>
                                    <div className="form-group col-md-12">
                                        <label htmlFor="first_name"> First Name </label>
                                        <input type="text" id="first_name" defaultValue={this.state.customer.first_name} onChange={(e) => this.handleInputChanges(e)} name="first_name" className="form-control" placeholder="Enter customer's first name" />
                                    </div>
                                    <div className="form-group col-md-12">
                                        <label htmlFor="last_name"> Last Name </label>
                                        <input type="text" id="last_name" defaultValue={this.state.customer.last_name} onChange={(e) => this.handleInputChanges(e)} name="last_name" className="form-control" placeholder="Enter customer's last name" />
                                    </div>
                                    <div className="form-group col-md-12">
                                        <label htmlFor="email"> Email </label>
                                        <input type="email" id="email" defaultValue={this.state.customer.email} onChange={(e) => this.handleInputChanges(e)} name="email" className="form-control" placeholder="Enter customer's email address" />
                                    </div>
                                    <div className="form-group col-md-12">
                                        <label htmlFor="phone"> Phone </label>
                                        <input type="text" id="phone" defaultValue={this.state.customer.phone} onChange={(e) => this.handleInputChanges(e)} name="phone" className="form-control" placeholder="Enter customer's phone number" />
                                    </div>
                                    <div className="form-group col-md-12">
                                        <label htmlFor="address"> Address </label>
                                        <input type="text" id="address" defaultValue={this.state.customer.address} onChange={(e) => this.handleInputChanges(e)} name="address" className="form-control" placeholder="Enter customer's address" />
                                    </div>
                                    <div className="form-group col-md-12">
                                        <label htmlFor="description"> Description </label>
                                        <input type="text" id="description" defaultValue={this.state.customer.description} onChange={(e) => this.handleInputChanges(e)} name="description" className="form-control" placeholder="Enter Description" />
                                    </div>
                                    <div className="form-group col-md-4 pull-right">
                                        <button className="btn btn-success" type="submit">
                                            Edit Customer </button>
                                        {loading &&
                                            <span className="fa fa-circle-o-notch fa-spin" />
                                        }
                                    </div>
                                </form>
                            </div>
                        </div>
                    </div>
                }
            </div>
        )
    }
...

Here, you created a form to edit the details of a particular customer, and then populated the input fields within that form with the customer’s details that your application’s state obtained. Similarly to the Create component, changes made to all the input fields will be handled by the handleInputChanges() method.

在这里,您创建了一个表单来编辑特定客户的详细信息,然后使用您的应用程序状态获得的客户详细信息填充该表单中的输入字段。 与Create组件类似,对所有输入字段所做的更改将由handleInputChanges()方法处理。

You can see the complete code for this file in this GitHub repository.

您可以在GitHub存储库中看到此文件的完整代码。

Save and exit Edit.tsx.

保存并退出Edit.tsx

To view the complete list of customers created within the application, you’ll create a new component within the ./src/components folder and name it Home.tsx:

要查看在应用程序中创建的客户的完整列表,您将在./src/components文件夹中创建一个新组件,并将其命名为Home.tsx

  • cd ./src/components

    cd ./src/components
  • nano Home.tsx

    纳米Home.tsx

Add the following content:

添加以下内容:

/src/components/Home.tsx
/src/components/Home.tsx
import * as React from 'react';
import { Link, RouteComponentProps } from 'react-router-dom';
import axios from 'axios';

interface IState {
    customers: any[];
}

export default class Home extends React.Component<RouteComponentProps, IState> {
    constructor(props: RouteComponentProps) {
        super(props);
        this.state = { customers: [] }
    }
    public componentDidMount(): void {
        axios.get(`http://localhost:5000/customers`).then(data => {
            this.setState({ customers: data.data })
        })
    }
    public deleteCustomer(id: number) {
        axios.delete(`http://localhost:5000/customers/${id}`).then(data => {
            const index = this.state.customers.findIndex(customer => customer.id === id);
            this.state.customers.splice(index, 1);
            this.props.history.push('/');
        })
    }
}

Here, you’ve imported React, axios, and other required components from React Router. You created two new methods within the Home component:

在这里,您已经从React Router导入了Reactaxios和其他必需的组件。 您在Home组件中创建了两个新方法:

  • componentDidMount(): The application invokes this method immediately after a component is mounted. Its responsibility here is to retrieve the list of customers and update the home page with it.

    componentDidMount() :安装组件后,应用程序立即调用此方法。 它的职责是检索客户列表并使用它更新主页。

  • deleteCustomer(): This method will accept an id as a parameter and will delete the details of the customer identified with that id from the database.

    deleteCustomer() :此方法将接受一个id作为参数,并将从数据库中删除使用该id标识的客户的详细信息。

Now add the render() method to display the table that holds the list of customers for the Home component:

现在添加render()方法以显示包含Home组件的客户列表的表:

/src/components/Home.tsx
/src/components/Home.tsx
...
public render() {
        const customers = this.state.customers;
        return (
            <div>
                {customers.length === 0 && (
                    <div className="text-center">
                        <h2>No customer found at the moment</h2>
                    </div>
                )}
                <div className="container">
                    <div className="row">
                        <table className="table table-bordered">
                            <thead className="thead-light">
                                <tr>
                                    <th scope="col">Firstname</th>
                                    <th scope="col">Lastname</th>
                                    <th scope="col">Email</th>
                                    <th scope="col">Phone</th>
                                    <th scope="col">Address</th>
                                    <th scope="col">Description</th>
                                    <th scope="col">Actions</th>
                                </tr>
                            </thead>
                            <tbody>
                                {customers && customers.map(customer =>
                                    <tr key={customer.id}>
                                        <td>{customer.first_name}</td>
                                        <td>{customer.last_name}</td>
                                        <td>{customer.email}</td>
                                        <td>{customer.phone}</td>
                                        <td>{customer.address}</td>
                                        <td>{customer.description}</td>
                                        <td>
                                            <div className="d-flex justify-content-between align-items-center">
                                                <div className="btn-group" style={{ marginBottom: "20px" }}>
                                                    <Link to={`edit/${customer.id}`} className="btn btn-sm btn-outline-secondary">Edit Customer </Link>
                                                    <button className="btn btn-sm btn-outline-secondary" onClick={() => this.deleteCustomer(customer.id)}>Delete Customer</button>
                                                </div>
                                            </div>
                                        </td>
                                    </tr>
                                )}
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>
        )
    }
...

In this code block, you retrieve the lists of customers from the application’s state as an array, iterate over it, and display it within an HTML table. You also add the customer.id parameter, which the method uses to identify and delete the details of a particular customer from the list.

在此代码块中,您将从应用程序状态作为数组检索客户列表,对其进行迭代,并将其显示在HTML表中。 您还添加了customer.id参数,该方法用于从列表中识别和删除特定客户的详细信息。

Save and exit Home.tsx.

保存并退出Home.tsx

You’ve adopted a statically typed principle for all the components created with this application by defining types for the components and props through the use of interfaces. This is one of the best approaches to using TypeScript for a React application.

通过使用接口定义组件和道具的类型,您已经为使用此应用程序创建的所有组件采用了静态类型化的原理。 这是将TypeScript用于React应用程序的最佳方法之一。

With this, you’ve finished creating all the required reusable components for the application. You can now update the app component with links to all the components that you have created so far.

这样,您就完成了为应用程序创建所有必需的可重用组件的操作。 现在,您可以使用指向到目前为止已创建的所有组件的链接来更新应用程序组件。

步骤4 —设置路由并更新应用程序的入口点 (Step 4 — Setting Up Routing and Updating the Entry Point of the Application)

In this step, you will import the necessary components from the React Router package and configure the App component to render different components depending on the route that is loaded. This will allow you to navigate through different pages of the application. Once a user visits a route, for example /create, React Router will use the path specified to render the contents and logic within the appropriate component defined to handle such route.

在此步骤中,您将从React Router包中导入必要的组件,并配置App组件以根据所加载的路线呈现不同的组件。 这将允许您浏览应用程序的不同页面。 一旦用户访问了一条路由(例如/create ,React Router就会使用指定的路径来呈现内容和逻辑,该内容和逻辑定义在处理该路由的适当组件中。

Navigate to ./src/App.tsx:

导航到./src/App.tsx

  • nano App.tsx

    纳米App.tsx

Then replace its content with the following:

然后将其内容替换为以下内容:

/src/App.tsx
/src/App.tsx
import * as React from 'react';
import './App.css';
import { Switch, Route, withRouter, RouteComponentProps, Link } from 'react-router-dom';
import Home from './components/Home';
import Create from './components/customer/Create';
import EditCustomer from './components/customer/Edit';

class App extends React.Component<RouteComponentProps<any>> {
  public render() {
    return (
      <div>
        <nav>
          <ul>
            <li>
              <Link to={'/'}> Home </Link>
            </li>
            <li>
              <Link to={'/create'}> Create Customer </Link>
            </li>
          </ul>
        </nav>
        <Switch>
          <Route path={'/'} exact component={Home} />
          <Route path={'/create'} exact component={Create} />
          <Route path={'/edit/:id'} exact component={EditCustomer} />
        </Switch>
      </div>
    );
  }
}
export default withRouter(App);

You imported all the necessary components from the React Router package and you also imported the reusable components for creating, editing, and viewing customers’ details.

您已从React Router软件包中导入了所有必需的组件,还导入了可重复使用的组件,用于创建,编辑和查看客户的详细信息。

Save and exit App.tsx.

保存并退出App.tsx

The ./src/index.tsx file is the entry point for this application and renders the application. Open this file and import React Router into it, then wrap the App component inside a BrowserRouter:

./src/index.tsx文件是此应用程序的入口点,并呈现该应用程序。 打开此文件并将React Router导入其中,然后将App组件包装在BrowserRouter

/src/index.tsx
/src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom'; 
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
    <BrowserRouter>
        <App />
    </BrowserRouter>
    , document.getElementById('root')
);
serviceWorker.unregister();

React Router uses the BrowserRouter component to make your application aware of the navigation, such as history and current path.

React Router使用BrowserRouter组件使您的应用程序了解导航,例如历史记录和当前路径。

Once you’ve finished editing Index.tsx, save and exit.

完成Index.tsx编辑Index.tsx ,保存并退出。

Lastly, you will use Bootstrap to add some style to your application. Bootstrap is a popular HTML, CSS, and JavaScript framework for developing responsive, mobile-first projects on the web. It allows developers to build an appealing user interface without having to write too much CSS. It comes with a responsive grid system that gives a web page a finished look that works on all devices.

最后,您将使用Bootstrap为您的应用程序添加一些样式。 Bootstrap是一种流行HTML,CSS和JavaScript框架,用于在网络上开发响应式,移动优先的项目。 它允许开发人员构建吸引人的用户界面,而无需编写过多CSS。 它带有响应式网格系统,可为网页提供在所有设备上均可使用的最终外观。

To include Bootstrap and styling for your application, replace the contents of ./src/App.css with the following:

要为您的应用程序包括Bootstrap和样式,请使用以下内容替换./src/App.css的内容:

/src/App.css
/src/App.css
@import 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css';

.form-wrapper {
  width: 500px;
  margin: 0 auto;
}
.App {
  text-align: center;
  margin-top: 30px;
}
nav {
  width: 300px;
  margin: 0 auto;
  background: #282c34;
  height: 70px;
  line-height: 70px;
}
nav ul li {
  display: inline;
  list-style-type: none;
  text-align: center;
  padding: 30px;
}
nav ul li a {
  margin: 50px 0;
  font-weight: bold;
  color: white;
  text-decoration: none;
}
nav ul li a:hover {
  color: white;
  text-decoration: none;
}
table {
  margin-top: 50px;
}
.App-link {
  color: #61dafb;
}
@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

You have used Bootstrap here to enhance the look and feel of the application by giving it a default layout, styles, and color. You have also added some custom styles, particularly to the navigation bar.

您已在此处使用Bootstrap通过为其提供默认布局,样式和颜色来增强应用程序的外观。 您还添加了一些自定义样式,尤其是在导航栏中。

Save and exit App.css.

保存并退出App.css

In this section, you have configured React Router to render the appropriate component depending on the route visited by the user and also added some styling to make the application more attractive to users. Next, you will test all the functionality implemented for the application.

在本节中,您已经配置了React Router来根据用户访问的路线来渲染适当的组件,并且还添加了一些样式以使应用程序对用户更具吸引力。 接下来,您将测试为该应用程序实现的所有功能。

步骤5 —运行您的应用程序 (Step 5 — Running Your Application)

Now that you have set up the frontend of this application with React and TypeScript by creating several reusable components, and also built a REST API with the json-server, you can run your app.

现在,您已经通过创建几个可重用的组件使用React和TypeScript设置了此应用程序的前端,并且还使用json-server构建了REST API,您可以运行您的应用程序。

Navigate back to the project’s root folder:

导航回到项目的根文件夹:

  • cd ~/typescript-react-app

    cd〜/ typescript-react-app

Next run the following command to start your app:

接下来运行以下命令来启动您的应用程序:

  • yarn start

    纱线开始

Note: Make sure your server is still running in the other terminal window. Otherwise, start it with: yarn server.

注意:确保服务器仍在另一个终端窗口中运行。 否则,从以下命令启动: yarn server

Navigate to http://localhost:3000 to view the application from your browser. Then proceed to click on the Create button and fill in the details of a customer.

导航到http://localhost:3000以从浏览器查看该应用程序。 然后继续单击“ 创建”按钮并填写客户的详细信息。

After entering the appropriate values in the input fields, click on the Create Customer button to submit the form. The application will redirect you back to your homepage once you’re done creating a new customer.

在输入字段中输入适当的值后,单击“ 创建客户”按钮以提交表单。 完成创建新客户后,该应用程序会将您重定向回首页。

Click the Edit Customer button for any of the rows and you will be directed to the page that hosts the editing functionality for the corresponding customer on that row.

单击任何行的“ 编辑客户”按钮,您将被转到托管该行相应客户的编辑功能的页面。

Edit the details of the customer and then click on Edit Customer to update the customer’s details.

编辑客户的详细信息,然后单击“ 编辑客户”以更新客户的详细信息。

You’ve run your application to ensure all the components are working. Using the different pages of your application, you’ve created and edited a customer entry.

您已经运行了应用程序以确保所有组件均正常工作。 使用应用程序的不同页面,您已经创建和编辑了客户条目。

结论 (Conclusion)

In this tutorial you built a customer list management app with React and TypeScript. The process in this tutorial is a deviation from using JavaScript as the conventional way of structuring and building applications with React. You’ve leveraged the benefits of using TypeScript to complete this front-end focused tutorial.

在本教程中,您使用ReactTypeScript构建了一个客户列表管理应用程序。 本教程中的过程与使用JavaScript作为使用React构建和构建应用程序的常规方式有所不同。 您已经利用使用TypeScript的好处来完成本前端重点教程。

To continue to develop this project, you can move your mock back-end server to a production-ready back-end technology like Express or Nest.js. Furthermore, you can extend what you have built in this tutorial by adding more features such as authentication and authorization with different tools like the Passport.js authentication library.

要继续开发该项目,您可以将模拟后端服务器移至ExpressNest.js等可用于生产的后端技术。 此外,您可以通过使用Passport.js身份验证库之类的不同工具添加更多功能(例如身份验证和授权)来扩展本教程中构建的内容。

You can find the complete source code for the project on GitHub.

您可以在GitHub上找到该项目的完整源代码。

翻译自: https://www.digitalocean.com/community/tutorials/how-to-build-a-customer-list-management-app-with-react-and-typescript

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值