📆

カレンダーアプリを作る(フロントエンド編④)

2025/04/11に公開

こちらの記事は前回の続きになります。
バックエンドを構築したので、フロントエンドと繋いでいきたいと思います。
バックエンドの構築についてはこちらの記事で記載しました。

実装

実装1 スケジュールを作成し、DBに保存する

schedules.tsファイルを作成し、スケジュールを作成する関数を作成します。
fetch 関数を使用して、指定されたURL(http://localhost:3000/schedules)に対してPOSTリクエストを送信します。
サーバーからのレスポンスを成形し、返します。

/src/components/Calendar/api/schedules.ts
import { MyEventsType } from "../types/hooks";

export const createSchedule = async (schedule: MyEventsType) => {
  try {
    const response = await fetch("http://localhost:3000/schedules", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(schedule),
    });
    const data = await response.json();
    const formatData = {
      id: data.id,
      title: data.title,
      start: new Date(data.start),
      end: new Date(data.end),
      allDay: data.allDay,
    };
    return formatData;
  } catch (e) {
    console.log(e);
  }
};

カスタムフックのonAddEventにてcreateScheduleを実行し、レスポンスデータを、setMyEventsに入れて配列を更新します。また、FullCalendarのaddEventでカレンダーに新しいスケジュールを登録します。

/src/components/Calendar/hooks/useCalendarFunc.tsx
import { createSchedule } from "../api/schedules";

export const useCalendarFunc = () => {
  ...

  /**
   * フォームに入力したスケジュールをカレンダーに登録する処理
   */
  const onAddEvent = async () => {
   ...
    const event = {
      title: eventsTitle,
      start: eventsStartDate,
      end: eventsEndDate,
      allDay: isAllDay,
    };

    const createdData = await createSchedule(event);
    if (!createdData) {
      alert("スケジュールの登録に失敗しました");
      return;
    }
    setMyEvents([...myEvents, createdData]);
    // カレンダーに予定を登録して表示するための処理。
    ref.current.getApi().addEvent(createdData);
  };

カレンダーとDBそれぞれに新しいスケジュールを登録することができました。


カレンダーに登録

DBに登録

実装2 DBに登録されているスケジュールをカレンダーに表示する

現状では、ステートにスケジュールを保持しているため、画面が更新されると消えてしまいます。
そのため、DBからスケジュールを取得し、カレンダーに表示します。
schedules.tsでgetAllSchedules関数を定義し、エクスポートします。

/src/components/Calendar/api/schedules.ts
import { MyEventsType } from "../types/hooks";

export const getAllSchedules = async () => {
  try {
    const response = await fetch(`http://localhost:3000/schedules/`, {
      method: "GET",
    });
    const data = await response.json();
    return data;
  } catch (e) {
    console.log(e);
  }
};

CalendarPage.tsxでuseEffectを使い、画面がマウントされた際にDBから全てのスケジュールを取得し、setMyEventsに入れてあげます。

/src/components/Calendar/CalendarPage.tsx
import { getAllSchedules } from "./api/schedules";

export const CalendarPage = () => {
  const {
    ...
    setMyEvents // 追加してない場合追加する
  } = useCalendarFunc();

  useEffect(() => {
    const schedules = async () => {
      try {
        const schedules = await getAllSchedules();
        setMyEvents(schedules);
      } catch (e) {
        console.error(e);
      }
    };
    schedules();
  }, []);
  return (

これでDBに登録されているスケジュールが表示され、画面が更新されてもデータを表示できるよになりました。

実装3 スケジュールを編集し、DBに登録されているデータを更新する

登録済みのスケジュールを編集し、DBのデータを更新しようと思います。

/src/components/Calendar/api/schedules.ts
import { MyEventsType } from "../types/hooks";

export const updateSchedule = async (id: string, schedule: MyEventsType) => {
  try {
    const response = await fetch(`http://localhost:3000/schedules/${id}`, {
      method: "PATCH",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(schedule),
    });
    const data = await response.json();
    return data;
  } catch (e) {
    console.log(e);
  }
};

カスタムフックのonEditEvent関数で、DBのデータ更新と、setMyEventsの更新を行います。

/src/components/Calendar/hooks/useCalendarFunc.tsx
import { createSchedule, updateSchedule } from "../api/schedules";

export const useCalendarFunc = () => {
  ...
  /**
   * カレンダーに登録済みのスケジュールを変更する
   */
  const onEditEvent = async () => {
    ...
    const data = {
      title: eventsTitle,
      allDay: isAllDay,
      start: eventsStartDate,
      end: eventsEndDate,
    };
    const updateData = await updateSchedule(eventsId, data);
    setMyEvents((prevEvents: MyEventsType[]) =>
      prevEvents.map((event: MyEventsType) => {
        if (event.id === updateData.id) {
          return {
            ...event,
            id: updateData.id,
            title: updateData.title,
            allDay: updateData.allDay,
            start: updateData.start,
            end: updateData.end,
          };
        }
        return event;
      })
    );
  };

問題なくカレンダーに登録されているデータとDBに登録されているデータを更新することができました。

カレンダーを更新


DBを更新

実装4 スケジュール選択し、DBから削除する

最後にDBからスケジュールを削除し、カレンダーに登録されている値も削除したいと思います。
schedule.tsにスケジュール削除関数を定義します。

/src/components/Calendar/api/schedules.ts
import { MyEventsType } from "../types/hooks";

export const deleteSchedule = async (id: string) => {
  try {
    const response = await fetch(`http://localhost:3000/schedules/${id}`, {
      method: "DELETE",
    });
    const data = await response.json();
    return data;
  } catch (e) {
    console.log(e);
  }
};

カスタムフックのonDeleteEvent関数でDBのデータとカレンダーに登録されているデータを削除します。
DBのデータを削除し、レスポンスが返ってきた場合にsetMyEventsからデータを削除します。

/src/components/Calendar/hooks/useCalendarFunc.tsx
import { createSchedule, updateSchedule } from "../api/schedules";

export const useCalendarFunc = () => {
  ...

  const onDeleteEvent = async () => {
    const res = await deleteSchedule(eventsId);
    if (!res) {
      alert("スケジュールの削除に失敗しました");
      return;
    }
    setMyEvents((prevEvent: MyEventsType[]) =>
      prevEvent.filter((event: MyEventsType) => event.id !== res.id)
    );
  };

問題なくデータが削除できました。

まとめ

デザインを整えて最終的には以下のようになりました。

カレンダー画面


スケジュール登録画面


スケジュール編集画面


スケジュール削除ダイアログ

このアプリでは、バックエンドとフロントエンドをフルスタックで構築しました。フロントエンドについてはある程度の知識がありましたが、バックエンドに関しては経験が少なく、試行錯誤を重ねながら開発を進めました。NestJSやPrismaについては、まだ改善の余地があると感じています。また、認証機能などを追加し、ユーザーごとにカレンダーを利用できるようにすることでより良いアプリにできると考えています。
フルスタックエンジニアを目指してこれからもアプリ開発に励んでいこうと思います。

Create Quest.Inc Tech Blog

Discussion