Chapter 06

Todoチェック機能を追加する

shitakemura
shitakemura
2022.02.13に更新

はじめに

本チャプターでは Todo が完了済みかどうかをチェックする機能を追加します。

インフラ&バックエンド

スキーマ定義を更新する

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

./infra-backend/graphql/schema.graphql
+ input ToggleTodoInput {
+   id: ID!
+   completed: Boolean!
+ }

type Mutation {
   ....
+  toggleTodo(toggleTodoInput: ToggleTodoInput!): Todo
}

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

./infra-backend
$ npm run codegen

Todo を更新する Lambda 関数を実装する

Lambda 関数toggleTodoを作成します。

./infra-backend/lambda/toggleTodo.ts
import { AppSyncResolverHandler } from "aws-lambda";
import {
  MutationToggleTodoArgs,
  Todo,
} from "../graphql/generated/generated-types";
import { DynamoDB } from "aws-sdk";

const docClient = new DynamoDB.DocumentClient();

export const handler: AppSyncResolverHandler<
  MutationToggleTodoArgs,
  Todo | null
> = async (event) => {
  const toggleTodoInput = event.arguments.toggleTodoInput;

  try {
    if (!process.env.TODO_TABLE) {
      console.log("TODO_TABLE was not specified");
      return null;
    }

    const data = await docClient
      .update({
        TableName: process.env.TODO_TABLE,
        Key: {
          id: toggleTodoInput.id,
        },
        UpdateExpression: "set completed = :c",
        ExpressionAttributeValues: {
          ":c": toggleTodoInput.completed,
        },
        ReturnValues: "ALL_NEW",
      })
      .promise();
    return data.Attributes as Todo;
  } 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 toggleTodoLambda = new lambdaNodeJs.NodejsFunction(
+     this,
+     "toggleTodoHandler",
+     {
+       entry: path.join(__dirname, "../lambda/toggleTodo.ts"),
+       ...commonLambdaNodeJsProps,
+     }
+   );

+   todoTable.grantReadWriteData(toggleTodoLambda);

    // DataSource
    ...
+   const toggleTodoDataSource = todoApi.addLambdaDataSource(
+     "toggleTodoDataSource",
+     toggleTodoLambda
+   );

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

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

./infra-backend
$ cdk diff
$ cdk deploy

フロントエンド

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

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

./frontend/graphql/ToggleTodo.graphql
mutation ToggleTodo($toggleTodoInput: ToggleTodoInput!) {
  toggleTodo(toggleTodoInput: $toggleTodoInput) {
    id
    title
    completed
    createdAt
  }
}

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

./frontend
$ npm run codegen

TodoItem コンポーネントにチェックボックスを追加する

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

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

export const TodoItem = ({ todo }: TodoItemProps) => {
  ...
  return (
    <HStack
      ...
      >
+     <HStack spacing={8}>
+       <Checkbox size='lg' isChecked={todoItem.completed} />
        <Text
          ...
          >
          {todoItem.title}
        </Text>
+     </HStack>
    </HStack>
  );
};

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

Todo チェック機能を実装する

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

./frontend/components/Todo/TodoInput.tsx
+ import { useCallback, useState } from "react";
+ import { HStack, Text, Checkbox } from "@chakra-ui/react";
 import {
  Todo,
+  useToggleTodoMutation,
} from "../../graphql/generated/generated-types";

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

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

+  const handleToggleTodo = useCallback(() => {
+    if (loading) return;
+    toggleTodo({
+      variables: {
+        toggleTodoInput: { id: todoItem.id, completed: !todoItem.completed },
+      },
+      onCompleted: () => {
+        setTodoItem({ ...todoItem, completed: !todoItem.completed });
+      },
+    });
+  }, [loading, todoItem, toggleTodo]);

  return (
    <HStack
      ...
      >
      <HStack spacing={8}>
        <Checkbox
          ...
+         onChange={handleToggleTodo}
        />
        <Text
          ...
          >
          {todoItem.title}
        </Text>
      </HStack>
    </HStack>
  );
};

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

以上で Todo チェック機能の追加は完了です。