著者は、 Write forDOnationsプログラムの一環として寄付を受け取るためにTechEducationFundを選択しました。

序章

TypeScript は、 JavaScript 開発者がアプリ、特にWebアプリケーションのコードを構造化および作成する方法に多くの改善をもたらしました。 JavaScriptのスーパーセットとして定義されたTypeScriptは、JavaScriptと同じように動作しますが、開発者がバグの少ない、またはバグのない、より大きく複雑なプログラムを構築できるように設計された追加機能を備えています。 TypeScriptはますます人気が高まっています。 AngularWebフレームワークのためにGoogleのような主要企業によって採用されました。 Nest.jsバックエンドフレームワークもTypeScriptで構築されました。

開発者としての生産性を向上させる方法の1つは、本番環境で既存のアプリを壊すことを心配せずに、新しい機能をできるだけ早く実装できることです。 これを実現するために、静的に型付けされたコードを書くことは、多くのベテラン開発者によって採用されているスタイルです。 TypeScriptのような静的に型付けされたプログラミング言語は、すべての変数とデータ型の関連付けを強制します。 文字列、整数、ブール値など。 静的に型付けされたプログラミング言語を使用する主な利点の1つは、型チェックがコンパイル時に完了するため、開発者は非常に早い段階でコードのエラーを確認できることです。

React はオープンソースのJavaScriptライブラリであり、開発者はこれを使用して、スケーラブルなWebアプリケーション用のハイエンドユーザーインターフェイスを作成します。 単一ページアプリケーション用にReactで構築された優れたパフォーマンスと動的なユーザーインターフェイスにより、開発者の間で人気のある選択肢となっています。

このチュートリアルでは、個別のREST APIバックエンドと、ReactおよびTypeScriptで構築されたフロントエンドを使用して顧客リスト管理アプリケーションを作成します。 json-serverという名前の偽のRESTAPIを使用してバックエンドを構築します。 これを使用して、CRUD(作成、読み取り、更新、および削除)バックエンドをすばやく設定します。 したがって、ReactとTypeScriptを使用してアプリケーションのフロントエンドロジックの処理に集中できます。

前提条件

このチュートリアルを完了するには、次のものが必要です。

ステップ1—TypeScriptのインストールとReactアプリケーションの作成

このステップでは、ノードパッケージマネージャー(npm)を使用して、TypeScriptパッケージをマシンにグローバルにインストールします。 その後、Reactとその依存関係もインストールし、開発サーバーを実行してReactアプリが機能していることを確認します。

まず、ターミナルを開き、次のコマンドを実行してTypeScriptをインストールします。

  1. npm install -g typescript

インストールプロセスが完了したら、次のコマンドを実行して、TypeScriptのインストールを確認します。

  1. tsc -v

マシンにインストールされている現在のバージョンが表示されます。

Output
Version 3.4.5

次に、 create-react-app ツールを使用してReactアプリケーションをインストールし、1つのコマンドでアプリケーションをセットアップします。 npxコマンドを使用します。これは、npm5.2以降に付属するパッケージランナーツールです。 create-react-appツールには、追加の構成を必要とせずにTypeScriptを操作するためのサポートが組み込まれています。 次のコマンドを実行して、typescript-react-appという名前の新しいReactアプリケーションを作成してインストールします。

  1. npx create-react-app typescript-react-app --typescript

上記のコマンドは、typescript-react-appという名前の新しいReactアプリケーションを作成します。 --typescriptフラグは、Reactコンポーネントのデフォルトのファイルタイプを.tsxに設定します。

このセクションを完了する前に、アプリケーションは1つのポートから別のポートに移動する必要があります。 これを行うには、 ReactRouterという名前のReactアプリケーション用のルーティングライブラリとそれに対応するTypeScript定義をインストールする必要があります。 yarnを使用して、このプロジェクトのライブラリとその他のパッケージをインストールします。 これは、特にReactアプリケーションの依存関係をインストールする場合に、yarnの方が高速であるためです。 新しく作成したプロジェクトフォルダーに移動し、次のコマンドを使用してReactRouterをインストールします。

  1. cd typescript-react-app
  2. yarn add react-router-dom

これで、プロジェクト内のルーティング機能を提供するReactRouterパッケージができました。 次に、次のコマンドを実行して、ReactRouterのTypeScript定義をインストールします。

  1. yarn add @types/react-router-dom

次に、ブラウザ用のPromisedベースのHTTPクライアントである axios をインストールして、アプリケーション内で作成するさまざまなコンポーネントからHTTPリクエストを実行するプロセスを支援します。

  1. yarn add axios

インストールプロセスが完了したら、次のコマンドで開発サーバーを起動します。

  1. yarn start

アプリケーションはhttp://localhost:3000で実行されます。

React application homepage

これで、TypeScriptが正常にインストールされ、新しいReactアプリケーションが作成され、アプリケーションのあるページから別のページへの移動を支援するためにReactRouterがインストールされました。 次のセクションでは、アプリケーションのバックエンドサーバーを設定します。

ステップ2—JSONサーバーを作成する

このステップでは、Reactアプリケーションがすばやく接続し、そのリソースを使用できる模擬サーバーを作成します。 このバックエンドサービスは、本番環境のアプリケーションには適していないことに注意してください。 Nest.js、Express、またはその他のバックエンドテクノロジーを使用して、本番環境でRESTfulAPIを構築できます。 json-serverは、プロトタイプを作成してバックエンドサーバーをモックする必要がある場合に便利なツールです。

npmまたはyarnのいずれかを使用して、json-serverをマシンにインストールできます。 これにより、プロジェクトを使用する必要がある場合はいつでも、プロジェクトの任意のディレクトリから利用できるようになります。 新しいターミナルウィンドウを開き、次のコマンドを実行して、プロジェクトディレクトリ内にいる間にjson-serverをインストールします。

  1. yarn global add json-server

次に、RESTAPIによって公開されるデータを含むJSONファイルを作成します。 このファイル(作成する)で指定されたオブジェクトの場合、CRUDエンドポイントが自動的に生成されます。 まず、serverという名前の新しいフォルダーを作成し、そのフォルダーに移動します。

  1. mkdir server
  2. cd server

次に、nanoを使用して、db.jsonという名前の新しいファイルを作成して開きます。

  1. nano db.json

次のコンテンツをファイルに追加します。

/server/db.json
{
    "customers": [
        {
            "id": 1,
            "first_name": "Customer_1",
            "last_name": "Customer_11",
            "email": "[email protected]",
            "phone": "00000000000",
            "address": "Customer_1 Address",
            "description": "Customer_1 description"
        },
        {
            "id": 2,
            "first_name": "Customer_2",
            "last_name": "Customer_2",
            "email": "[email protected]",
            "phone": "00000000000",
            "address": "Customer_2 Adress",
            "description": "Customer_2 Description"
        }
    ]
}

JSON構造は、2つのデータセットが割り当てられた顧客オブジェクトで構成されています。 各顧客は、iddescriptionfirst_namelast_nameemailphone、および7つのプロパティで構成されます。 address

ファイルを保存して終了します。

デフォルトでは、json-serverはポート3000で実行されます。これは、Reactアプリケーションが実行されるのと同じポートです。 競合を回避するために、json-serverのデフォルトポートを変更できます。 これを行うには、アプリケーションのルートディレクトリに移動します。

  1. cd ~/typescript-react-app

お好みのテキストエディタでアプリケーションを開き、json-server.jsonという名前の新しいファイルを作成します。

  1. nano json-server.json

次に、以下を挿入してポート番号を更新します。

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

これはjson-serverの構成ファイルとして機能し、サーバーが常に指定されたポートで実行されるようにします。

ファイルを保存して終了します。

サーバーを実行するには、次のコマンドを使用します。

  1. json-server --watch server/db.json

これにより、ポート5000json-serverが開始されます。 ブラウザでhttp://localhost:5000/customersに移動すると、サーバーに顧客リストが表示されます。

Customer list shown by json-server

json-serverの実行プロセスを合理化するために、package.jsonserverという名前の新しいプロパティでscriptsオブジェクトに更新できます。

/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"
  },
...
}

ファイルを保存して終了します。

これで、json-serverを起動したいときはいつでも、ターミナルからyarn serverを実行するだけです。

このアプリケーションのバックエンドサーバーとして使用する単純なRESTAPIを作成しました。 また、RESTAPIのデフォルトデータとして使用されるカスタマーJSONオブジェクトを作成しました。 最後に、json-serverを搭載したバックエンドサーバーの代替ポートを構成しました。 次に、アプリケーション用に再利用可能なコンポーネントを作成します。

ステップ3—再利用可能なコンポーネントの作成

このセクションでは、アプリケーションに必要なReactコンポーネントを作成します。 これには、データベース内の特定の顧客の詳細をそれぞれ作成、表示、および編集するためのコンポーネントが含まれます。 また、アプリケーション用にいくつかのTypeScriptインターフェイスを構築します。

まず、Reactアプリケーションを実行しているターミナルに戻り、CTRL + Cで開発サーバーを停止します。 次に、./src/フォルダーに移動します。

  1. cd ./src/

次に、その中にcomponentsという名前の新しいフォルダーを作成し、新しいフォルダーに移動します。

  1. mkdir components
  2. cd components

新しく作成したフォルダー内に、customerフォルダーを作成し、そのフォルダーに移動します。

  1. mkdir customer
  2. cd customer

次に、Create.tsxおよびEdit.tsxという名前の2つの新しいファイルを作成します。

  1. touch Create.tsx Edit.tsx

これらのファイルはReactの再利用可能なコンポーネントであり、フォームをレンダリングし、顧客の詳細をそれぞれ作成および編集するためのすべてのビジネスロジックを保持します。

テキストエディタで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;
}

ここでは、Reactaxios、およびルーティングに必要なその他の必要なコンポーネントをReactRouterパッケージからインポートしました。 その後、IValuesIFormStateという名前の2つの新しいインターフェイスを作成しました。 TypeScriptインターフェイスは、オブジェクトに渡す必要のある特定のタイプの値を定義し、アプリケーション全体で一貫性を保つのに役立ちます。 これにより、プログラムにバグが発生する可能性が低くなります。

次に、React.Componentを拡張するCreateコンポーネントを作成します。 IFormStateインターフェースの直後の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)

ここでは、TypescriptでReactコンポーネントを定義しました。 この場合、Createクラスコンポーネントは、タイプRouteComponentPropsprops(「プロパティ」の略)を受け入れ、タイプIFormStateの状態を使用します。 次に、コンストラクター内で、stateオブジェクトを初期化し、顧客のレンダリング値を表すすべての変数を定義しました。

次に、コンストラクターの直後のCreateクラスコンポーネント内にこれらのメソッドを追加します。 これらのメソッドを使用して、顧客フォームを処理し、入力フィールドのすべての変更を処理します。

/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)
...

processFormSubmission()メソッドは、アプリケーションの状態から顧客の詳細を受け取り、axiosを使用してデータベースに投稿します。 handleInputChanges()は、React.FormEventを使用してすべての入力フィールドの値を取得し、this.setState()を呼び出してアプリケーションの状態を更新します。

次に、handleInputchanges()メソッドの直後のCreateクラスコンポーネント内にrender()メソッドを追加します。 このrender()メソッドは、アプリケーションで新しい顧客を作成するためのフォームを表示します。

/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>
      )
  }
...

ここでは、first_namelast_nameemailphoneaddressの値を保持する入力フィールドを持つフォームを作成しました。 、およびお客様のdescription。 各入力フィールドには、キーストロークごとに実行されるメソッドhandleInputChanges()があり、入力フィールドから取得した値でReactstateを更新します。 さらに、アプリケーションの状態に応じて、submitSuccessという名前のブール変数が、新しい顧客を作成する前後にアプリケーションが表示するメッセージを制御します。

このファイルの完全なコードは、このGitHubリポジトリで確認できます。

Create.tsxを保存して終了します。

アプリケーションのCreateコンポーネントファイルに適切なロジックを追加したので、次にEditコンポーネントファイルのコンテンツを追加します。

customerフォルダー内のEdit.tsxファイルを開き、次のコンテンツを追加してReactaxiosをインポートし、TypeScriptインターフェイスを定義します。

/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;
}

Createコンポーネントと同様に、必要なモジュールをインポートして、それぞれIValuesおよびIFormStateインターフェースを作成します。 IValuesインターフェイスは、入力フィールドの値のデータ型を定義しますが、IFormStateを使用して、アプリケーションの状態オブジェクトの予想される型を宣言します。

次に、次に示すように、IFormStateインターフェイスブロックの直後にEditCustomerクラスコンポーネントを作成します。

/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)

このコンポーネントは、RouteComponentProps<any>IFormStateのインターフェースをパラメーターとして受け取ります。 <any>RouteComponentPropsに追加することを使用します。これは、React Routerがパスパラメーターを解析するたびに、データのタイプがnumberであるかどうかを確認するためのタイプ変換を行わないためです。またはstring。 顧客のuniqueIdのパラメーターを期待しているので、anyを使用する方が安全です。

次に、コンポーネント内に次のメソッドを追加します。

/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)

まず、componentDidMount()メソッドを追加します。これは、コンポーネントの作成時に呼び出されるライフサイクルメソッドです。 このメソッドは、ルートパラメーターから取得したidを取得して特定の顧客をパラメーターとして識別し、それを使用してデータベースから詳細を取得し、フォームに入力します。 さらに、フォームの送信を処理し、入力フィールドの値に加えられた変更を処理するメソッドを追加します。

最後に、Editコンポーネントにrender()メソッドを追加します。

/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>
        )
    }
...    

ここでは、特定の顧客の詳細を編集するためのフォームを作成し、そのフォーム内の入力フィールドに、アプリケーションの状態で取得した顧客の詳細を入力しました。 Createコンポーネントと同様に、すべての入力フィールドに加えられた変更は、handleInputChanges()メソッドによって処理されます。

このファイルの完全なコードは、このGitHubリポジトリで確認できます。

Edit.tsxを保存して終了します。

アプリケーション内で作成された顧客の完全なリストを表示するには、./src/componentsフォルダー内に新しいコンポーネントを作成し、Home.tsxという名前を付けます。

  1. cd ./src/components
  2. nano 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('/');
        })
    }
}

ここでは、Reactaxios、およびその他の必要なコンポーネントをReactRouterからインポートしました。 Homeコンポーネント内に2つの新しいメソッドを作成しました。

  • componentDidMount():アプリケーションは、コンポーネントがマウントされた直後にこのメソッドを呼び出します。 ここでの責任は、顧客のリストを取得し、それを使用してホームページを更新することです。
  • deleteCustomer():このメソッドは、idをパラメーターとして受け入れ、そのidで識別される顧客の詳細をデータベースから削除します。

次に、render()メソッドを追加して、Homeコンポーネントの顧客のリストを保持するテーブルを表示します。

/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>
        )
    }
...

このコードブロックでは、アプリケーションの状態から顧客のリストを配列として取得し、それを反復処理して、HTMLテーブル内に表示します。 また、customer.idパラメーターを追加します。このパラメーターを使用して、特定の顧客の詳細を識別し、リストから削除します。

Home.tsxを保存して終了します。

インターフェイスを使用してコンポーネントと小道具のタイプを定義することにより、このアプリケーションで作成されたすべてのコンポーネントに静的に型付けされた原則を採用しました。 これは、ReactアプリケーションにTypeScriptを使用するための最良のアプローチの1つです。

これで、アプリケーションに必要なすべての再利用可能なコンポーネントの作成が完了しました。 これで、これまでに作成したすべてのコンポーネントへのリンクを使用してアプリコンポーネントを更新できます。

ステップ4—ルーティングの設定とアプリケーションのエントリポイントの更新

このステップでは、React Routerパッケージから必要なコンポーネントをインポートし、ロードされるルートに応じて異なるコンポーネントをレンダリングするようにAppコンポーネントを構成します。 これにより、アプリケーションのさまざまなページをナビゲートできます。 ユーザーが/createなどのルートにアクセスすると、React Routerは指定されたパスを使用して、そのようなルートを処理するために定義された適切なコンポーネント内のコンテンツとロジックをレンダリングします。

./src/App.tsxに移動します。

  1. nano 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);

React Routerパッケージから必要なすべてのコンポーネントをインポートし、顧客の詳細を作成、編集、および表示するための再利用可能なコンポーネントもインポートしました。

App.tsxを保存して終了します。

./src/index.tsxファイルは、このアプリケーションのエントリポイントであり、アプリケーションをレンダリングします。 このファイルを開き、React Routerをインポートしてから、AppコンポーネントをBrowserRouter内にラップします。

/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は、BrowserRouterコンポーネントを使用して、履歴や現在のパスなどのナビゲーションをアプリケーションに認識させます。

Index.tsxの編集が終了したら、保存して終了します。

最後に、Bootstrapを使用して、アプリケーションにスタイルを追加します。 Bootstrap は、レスポンシブなモバイルファーストプロジェクトをWeb上で開発するための、人気のあるHTML、CSS、およびJavaScriptフレームワークです。 これにより、開発者は、CSSをあまり記述しなくても、魅力的なユーザーインターフェイスを構築できます。 レスポンシブグリッドシステムが付属しており、すべてのデバイスで機能する仕上がりの外観をWebページに提供します。

アプリケーションにブートストラップとスタイリングを含めるには、./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);
  }
}

ここでは、Bootstrapを使用して、デフォルトのレイアウト、スタイル、および色をアプリケーションに与えることにより、アプリケーションのルックアンドフィールを強化しています。 また、特にナビゲーションバーにいくつかのカスタムスタイルを追加しました。

App.cssを保存して終了します。

このセクションでは、ユーザーがアクセスしたルートに応じて適切なコンポーネントをレンダリングするようにReact Routerを構成し、アプリケーションをユーザーにとってより魅力的なものにするためのスタイルを追加しました。 次に、アプリケーションに実装されているすべての機能をテストします。

ステップ5—アプリケーションの実行

いくつかの再利用可能なコンポーネントを作成してReactとTypeScriptを使用してこのアプリケーションのフロントエンドを設定し、json-serverを使用してRESTAPIを構築したので、アプリを実行できます。

プロジェクトのルートフォルダに戻ります。

  1. cd ~/typescript-react-app

次に、次のコマンドを実行してアプリを起動します。

  1. yarn start

注:サーバーが他のターミナルウィンドウでまだ実行されていることを確認してください。 それ以外の場合は、yarn serverで開始します。

http://localhost:3000に移動して、ブラウザーからアプリケーションを表示します。 次に、作成ボタンをクリックして、顧客の詳細を入力します。

Create customer page

入力フィールドに適切な値を入力した後、顧客の作成ボタンをクリックしてフォームを送信します。 新しい顧客の作成が完了すると、アプリケーションはホームページにリダイレクトします。

View customers page

いずれかの行の顧客の編集ボタンをクリックすると、その行の対応する顧客の編集機能をホストするページに移動します。

Edit customer page

顧客の詳細を編集し、顧客の編集をクリックして顧客の詳細を更新します。

アプリケーションを実行して、すべてのコンポーネントが機能していることを確認しました。 アプリケーションのさまざまなページを使用して、顧客エントリを作成および編集しました。

結論

このチュートリアルでは、ReactTypeScriptを使用して顧客リスト管理アプリを作成しました。 このチュートリアルのプロセスは、Reactを使用してアプリケーションを構造化および構築する従来の方法としてJavaScriptを使用することとは異なります。 TypeScriptを使用する利点を活用して、このフロントエンドに焦点を当てたチュートリアルを完了しました。

このプロジェクトの開発を継続するには、モックバックエンドサーバーをExpressNest.jsなどの本番環境に対応したバックエンドテクノロジーに移行できます。 さらに、 Passport.js 認証ライブラリなどのさまざまなツールを使用して認証や承認などの機能を追加することにより、このチュートリアルで構築したものを拡張できます。

プロジェクトの完全なソースコードはGitHubにあります。