Chapter 07

Todo削除機能を追加する

shitakemura
shitakemura
2022.02.13に更新

はじめに

本チャプターでは Todo の削除機能を追加します。

インフラ&バックエンド

スキーマ定義を更新する

Todo を更新するためのミューテーションdeleteTodoを追加します。

./infra-backend/graphql/schema.graphql
type Mutation {
  ...
+ deleteTodo(id: ID!): Boolean
}

型定義ファイルを自動生成する

./infra-backend
$ npm run codegen

Todo を削除する Lambda 関数を実装する

Lambda 関数deleteTodoを作成します。

./infra-backend/lambda/deleteTodo.ts
import { AppSyncResolverHandler } from "aws-lambda";
import { MutationDeleteTodoArgs } from "../graphql/generated/generated-types";
import { DynamoDB } from "aws-sdk";

const docClient = new DynamoDB.DocumentClient();

export const handler: AppSyncResolverHandler<
  MutationDeleteTodoArgs,
  Boolean | null
> = async (event) => {
  const id = event.arguments.id;

  try {
    if (!process.env.TODO_TABLE) {
      console.log("TODO_TABLE was not specified");
      return null;
    }
    await docClient
      .delete({
        TableName: process.env.TODO_TABLE,
        Key: { id: id },
      })
      .promise();
    return true;
  } catch (err) {
    console.error(`DynamoDB error: ${err}`);
    return null;
  }
};

AWS CDK を更新して、作成した Lambda 関数をデプロイする

CDK を更新します

./infra-backend/lib/infra-backend-stack.ts
export class InfraBackendStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Lambda function
    ...
+   const deleteTodoLambda = new lambdaNodeJs.NodejsFunction(
+     this,
+     "deleteTodoHandler",
+     {
+       entry: path.join(__dirname, "../lambda/deleteTodo.ts"),
+       ...commonLambdaNodeJsProps,
+     }
+   );

+   todoTable.grantReadWriteData(deleteTodoLambda);

    // DataSource
    ...
+   const deleteTodoDataSource = todoApi.addLambdaDataSource(
+     "deleteTodoDataSource",
+     deleteTodoLambda
+   );

    // Resolever
    ...
+   deleteTodoDataSource.createResolver({
+     typeName: "Mutation",
+     fieldName: "deleteTodo",
+   });
  }
}

cdk diffcdk deployを実行してデプロイを行います。

./infra-backend
$ cdk diff
$ cdk deploy

フロントエンド

GraphQL ミューテーションを定義する

Todo 削除時に呼びだすミューテーションDeleteTodoの定義ファイルを作成します。

./frontend/graphql/DeleteTodo.graphql
mutation DeleteTodo($id: ID!) {
  deleteTodo(id: $id)
}

型定義ファイルを生成する

./frontend
$ npm run codegen

TodoItem コンポーネントに削除アイコンを追加する

Chakra UI icons の DeleteIcon を使って追加します。

./frontend/components/Todo/TodoItem.tsx
+ import { DeleteIcon } from "@chakra-ui/icons";

export const TodoItem = ({ todo }: TodoItemProps) => {
  ...
  return (
    <HStack
      ...
      >
     <HStack spacing={8}>
      ...
     </HStack>
+    <DeleteIcon color='blue.500' boxSize={5} _hover={{ boxSize: 6 }} />
    </HStack>
  );
};

npm run devを実行して、画面を確認します。

Todo 削除機能を実装する

GraphQL Code Generatorで作成したuseDeleteTodoMutation()を使用して、Todo チェック機能を実装します。

./frontend/components/Todo/TodoInput.tsx
- import { HStack, Text, Checkbox } from "@chakra-ui/react";
+ import { HStack, Text, Checkbox, Spinner } from "@chakra-ui/react";

 import {
  Todo,
  useToggleTodoMutation,
+ useDeleteTodoMutation,
} from "../../graphql/generated/generated-types";

export const TodoItem = ({ todo }: TodoItemProps) => {
- const [todoItem, setTodoItem] = useState<Todo>(todo);
+ const [todoItem, setTodoItem] = useState<Todo | null>(todo);

- const [toggleTodo, { loading }] = useToggleTodoMutation();
+ const [toggleTodo, toggleTodoStatus] = useToggleTodoMutation();

+ const [deleteTodo, deleteTodoStatus] = useDeleteTodoMutation();

  const handleToggleTodo = useCallback(() => {
-   if (loading) return
+   if (!todoItem) return;
+   if (toggleTodoStatus.loading) return;
    toggleTodo({
      ...
    });
- }, [loading, todoItem, toggleTodo]);
+ }, [toggleTodoStatus.loading, todoItem, toggleTodo]);

+ const handleDeleteTodo = useCallback(() => {
+   if (!todoItem) return;
+   if (deleteTodoStatus.loading) return;
+   deleteTodo({
+     variables: { id: todoItem.id },
+     onCompleted: () => setTodoItem(null),
+   });
+ }, [deleteTodoStatus, todoItem, deleteTodo]);

+ if (!todoItem) return null;

  return (
    <HStack
      ...
      >
      <HStack spacing={8}>
        ...
      </HStack>
-     <DeleteIcon color='blue.500' boxSize={5} _hover={{ boxSize: 6 }} />
+     {deleteTodoStatus.loading ? (
+       <Spinner
+         size='md'
+         thickness='4px'
+         emptyColor='gray.200'
+         color='blue.500'
+       />
+     ) : (
+       <DeleteIcon
+         color='blue.500'
+         boxSize={5}
+         _hover={{ boxSize: 6 }}
+         onClick={handleDeleteTodo}
+       />
+     )}
    </HStack>
  );
};

npm run devを実行して動作を確認します。

以上で Todo 削除機能の追加は完了です。