使用GraphQL、Go、React和TypeScript开发ToDo应用程序(前端部分)

这篇文章是 HRBrain Advent Calendar 2023 中第八天的文章。

 

首先

为了学习GraphQL,我尝试创建了一个Todo应用程序,并进行了一次实践。

在本文中,作为第二部分,我总结了使用GraphQL查询进行Todo应用程序前端开发的步骤。

请参阅部分1的后端编辑内容。

 

下面是一个潜在的中国的翻译选项:
所使用的技术栈

编程语言:Go、TypeScript
库:React、gqlgen、codegen、xorm
数据库:Postgres

gqlgen和codegen是能根据GraphQL模式定义自动生成Go和TypeScript代码的库。
xorm是Go的ORM库,使用Postgres作为数据库。

待办事项应用的概述

todoapp.gif

请参考适当的源代码以解释与GraphQL相关的部分!?

 

目录结构

以下是目录结构,并且在本文中我们将创建前端应用程序代码。

.
├── app                                            - バックエンドのアプリケーションコード
│   ├── Dockerfile
│   ├── go.mod
│   ├── go.sum
│   ├── gqlgen.yml                                 - gqlgenの設定ファイル
│   ├── graph                                      - GraphQL関連のコード
│   │   ├── generated.go                           - gqlgenによって自動生成されるGoのコード
│   │   ├── model                                  - GraphQLモデルを定義するGoのコード
│   │   │   ├── models_gen.go                      - gqlgenによって自動生成されるモデルのコード
│   │   │   └── todo.go
│   │   ├── resolver.go                            - GraphQLリゾルバのインターフェースを定義する
│   │   └── schema.resolvers.go                    - リゾルバインターフェースを具体的に実装するコード
│   ├── infrastructure
│   │   └── todo.go                                - インフラのTodoモデル
│   ├── server.go                                  - サーバーコードのエントリーポイント
│   ├── sql
│   │   └── init.sql                               - 初期化SQLスクリプト
│   └── tools.go                                   - gqlgenをインストール用のファイル
│
├── front                                          - フロントエンドのアプリケーションコード
│   ├── Dockerfile
│   ├── codegen.yml                                - codegen(フロントのGraphQLコード生成)の設定ファイル
│   ├── graphql                                    - GraphQLのクエリやスキーマを定義
│   │   ├── mutation                               - データを変更するGraphQLのmutation定義
│   │   │   ├── createTodo.graphql
│   │   │   ├── deleteTodo.graphql
│   │   │   └── updateTodoStatus.graphql
│   │   └── query                                  - データを取得するGraphQLのquery定義
│   │       └── getAllTodos.graphql
│   ├── node_modules
│   ├── package-lock.json
│   ├── package.json
│   ├── public
│   ├── src                                        - アプリケーションのソースコード
│   │   ├── App.tsx                                - メインのアプリケーションコンポーネント
│   │   ├── components                             - 再利用可能なUIコンポーネント
│   │   │   ├── CreateTodoForm.tsx
│   │   │   └── TodoList.tsx
│   │   ├── index.tsx                              - アプリケーションのエントリーポイント
│   │   └── types                                  - TypeScriptの型定義
│   │       └── gen                                - codegenによって自動生成される型定義
│   │           ├── api.ts                         - APIの型定義
│   │           └── possibleTypes-result.ts
│   └── tsconfig.json
│
├── docker-compose.yml
└── schema.graphqls                                - 共通のGraphQLスキーマを定義

Todo应用的实践操作。

现在,让我们解释一下使用GraphQL进行Todo应用程序的前端开发步骤。

前端开发流程概述

    1. 项目设置

 

    1. GraphQL Code Generator的配置

 

    1. GraphQL 查询和变更的定义

 

    1. 通过graphql-codegen自动生成代码

 

    1. Apollo Client的配置

 

    组件的实现

1. 项目的建立

首先,创建一个新目录,并创建一个React应用程序的模板。您可以使用create-react-app工具和TypeScript模板来开始项目。

cd ~/go/src/github.com/[username]/todoapp-graphql-go-react
mkdir front
cd front
npx create-react-app front --template typescript

接下来,我们需要安装GraphQL所需的软件包。

npm install graphql
npm install --save-dev @graphql-codegen/cli @graphql-codegen/schema-ast @graphql-codegen/fragment-matcher @graphql-codegen/typescript @graphql-codegen/typescript-operations @apollo/client @graphql-codegen/typescript-react-apollo

2. GraphQL Code Generator的配置

接下来,创建codegen.yml文件,并进行GraphQL Code Generator的配置。该工具可以根据GraphQL查询和模式自动生成TypeScript类型定义和React Hooks。

# GraphQLスキーマとドキュメントのソースを定義
schema:
  - ../*.graphqls

documents:
  - ./graphql/mutation/*.graphql
  - ./graphql/query/*.graphql

# 生成される出力とプラグインの設定
generates:
  # スキーマをAST形式で生成
  graphql/schema.graphql:
    plugins:
      - schema-ast

  # フラグメントマッチャーの生成
  src/types/gen/possibleTypes-result.ts:
    plugins:
      - fragment-matcher
    config:
      apolloClientVersion: 3

  # TypeScriptの型、操作、およびReact hooksの生成
  src/types/gen/api.ts:
    plugins:
      - typescript
      - typescript-operations
      - typescript-react-apollo
    config:
      withComponent: false
      withHooks: true

# グローバル設定
config:
  gqlImport: '@apollo/client#gql'
  scalars:
    Date: string
    NumberID: number
    Time: string
    Version: number

这个设置文件将根据GraphQL模式生成所需的类型和组件框架。

3. GraphQL 查询和变更的定义

GraphQL定义了查询(获取数据)和变异(更新数据)。它们是指定向服务器请求哪些数据或执行哪些操作的重要组成部分。

查询的示例

# GetAllTodosクエリは、サーバーから全てのTodo項目を取得するために使用されます。
query GetAllTodos {
  todos {
    id    # 各Todo項目のユニークなID
    text  # Todo項目のテキスト内容
    done  # Todoが完了したかどうかの状態
  }
}

突变的例子

# CreateTodoミューテーションは、新しいTodo項目を作成するために使用されます。
mutation CreateTodo($todoInput: CreateTodoInput!) {
  createTodo(todoInput: $todoInput) {
    id    # 作成されたTodo項目のID
    text  # 作成されたTodo項目のテキスト
    done  # 作成されたTodoの状態(デフォルトでは未完了)
  }
}

这些查询和变更定义了应用程序如何在服务器和应用程序之间交换数据。

4. 使用 graphql-codegen 进行代码自动生成。

然后,使用GraphQL代码生成器,根据上述定义的查询和突变生成代码。

npm run graphql-codegen --config codegen.yml

通过该命令,将自动创建所需的类型和钩子来操作API,大大加快开发流程。

5. Apollo Client 的配置

Apollo Client 是一個用於管理 React 應用程式和 GraphQL 伺服器之間通訊的函式庫。在這裡,我們會設定 Apollo Client 並建立與 GraphQL 伺服器的連接。

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';

// Apollo Clientの設定
const client = new ApolloClient({
  uri: 'http://localhost:8081/query',  // GraphQLサーバーのエンドポイント
  cache: new InMemoryCache(),          // クライアントサイドのキャッシュ管理
});

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
  <React.StrictMode>
    <ApolloProvider client={client}>   {/* Apollo ClientをReactアプリケーション全体で利用可能にする */}
      <App />
    </ApolloProvider>
  </React.StrictMode>
);

通過此設置,應用程式可以向GraphQL服務器發送查詢並獲取數據。

6. 组件的实现

最后,我们将实现Todo应用程序的主要用户界面组件。下面展示了显示Todo列表和添加新Todo的表单的示例实现。

待办事项列表组件的代码

import React from 'react';
import { DeleteTodoDocument, useGetAllTodosQuery, UpdateTodoStatusDocument } from '../types/gen/api';
import { useMutation } from '@apollo/client';

export const TodoList: React.FC = () => {
  const { data, loading, error, refetch } = useGetAllTodosQuery();  // サーバーからTodoリストを取得
  const [updateTodoStatus] = useMutation(UpdateTodoStatusDocument); // Todoステータスを更新するミューテーション
  const [deleteTodo] = useMutation(DeleteTodoDocument);             // Todoを削除するミューテーション

  // Todoのステータスを更新する関数
  const handleUpdateTodoStatus = async (todoId: string, done: boolean) => {
    try {
      await updateTodoStatus({ variables: { todoId, done } });
      refetch();
    } catch (error) {
      console.error('Error updating todo status: ', error);
    }
  };

  // Todoを削除する関数
  const handleDeleteTodo = async (todoId: string) => {
    try {
      await deleteTodo({ variables: { todoId } });
      refetch();
    } catch (error) {
      console.error('Error deleting todo: ', error);
    }
  };

  // データの読み込み中、またはエラーがある場合の処理
  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  // TodoリストのUIレンダリング
  return (
    <div>
      <h2>Todoリスト</h2>
      {/* 未完了のTodo項目をリストアップ */}
      <h3>未完了のTODO</h3>
      <ul>
        {data?.todos.map(
          (todo) =>
            todo.done || ( /* 未完了のTodoのみ表示 */
              <li key={todo.id}>
                {todo.text}
                <button onClick={() => handleUpdateTodoStatus(todo.id, true)}>完了</button>
                <button onClick={() => handleDeleteTodo(todo.id)}>削除</button>
              </li>
            )
        )}
      </ul>

     {/* 完了したTodo項目をリストアップ */}
      <h3>完了したTODO</h3>
      <ul>
        {data?.todos.map(
          (todo) =>
            todo.done && ( /* 完了したTodoのみ表示 */
              <li key={todo.id}>
                {todo.text}
                <button onClick={() => handleUpdateTodoStatus(todo.id, false)}>未完了に戻す</button>
                <button onClick={() => handleDeleteTodo(todo.id)}>削除</button>
              </li>
            )
        )}
      </ul>
    </div>
  );
};

创建Todo表单的代码

import React, { useState } from 'react';
import { useMutation } from '@apollo/client';
import { CreateTodoDocument, GetAllTodosDocument, GetAllTodosQuery } from '../types/gen/api';

function CreateTodoForm() {
  const [text, setText] = useState(''); // 新しいTodoのテキストを保持するstate
  const [createTodo] = useMutation(CreateTodoDocument, {
    // 新しいTodoを追加した後、キャッシュを更新してUIを自動的に反映させる
    update(cache, { data: { createTodo } }) {
      const existingTodos = cache.readQuery<GetAllTodosQuery>({ query: GetAllTodosDocument });

      if (existingTodos && createTodo) {
        cache.writeQuery({
          query: GetAllTodosDocument,
          data: { todos: [...existingTodos.todos, createTodo] },
        });
      }
    },
  });

  // フォームのサブミット処理
  const handleSubmit = async (event: { preventDefault: () => void }) => {
    event.preventDefault();

    if (!text.trim()) return;

    try {
      const todoInput = { text };
      await createTodo({ variables: { todoInput } });

      setText('');
    } catch (error) {
      console.error('Error adding todo: ', error);
    }
  };

  // Todo作成フォームのUIレンダリング
  return (
    <form onSubmit={handleSubmit}>
      <input type='text' value={text} onChange={(event) => setText(event.target.value)} placeholder='Add new todo' />
      <button type='submit'>Add</button>
    </form>
  );
}

export default CreateTodoForm;

这是使用GraphQL开发Todo应用前端的基本步骤。根据需要,可以参考各个技术的官方文档,了解每个步骤的详细说明和技术的基本知识!?

最后

虽然这只是一个简单的Todo应用程序开发,但通过实际操作学习GraphQL的基本部分非常有价值!尽管仍有些地方还没完全掌握,但我将继续学习下去。感谢大家一直以来的阅读!

在HRBrain我们正在招募愿意和我们合作的伙伴。如果您有兴趣,请务必申请。

 

请参考以下链接。

 

广告
将在 10 秒后关闭
bannerAds