Guide
State management in Cal.com

State management in Cal.com

In this guide, we will analyse how the state is managed in Cal.com.

We have to take a closer look at operations such as:

  1. Bookings listing page
  2. Event types listing page
  3. Add/Edit a new event type
  4. Add/Edit availability

These operations are specific to Cal.com, these operations vary based on an application. In Lobechat, to understand how how state is managed, you would look at operations such as add a new assistant, loading a chat etc.,

At the time of writing this guide, Cal.com used pages route and with a migration in place to use app router. Through out these guides, you will see references pointing at pages router. Be sure to check out the future folder in cal.com source as this shows the migration towards app folder.

[C: Bookings listing page]

[Insert screenshot of bookings page]

Cal.com uses Next.js pages router, there a folder named bookings inside pages folder and in the status.tsx , you will find the below code:

import type { GetStaticPaths } from "next";

import PageWrapper from "@components/PageWrapper";

import { validStatuses } from "~/bookings/lib/validStatuses";
import BookingsListingView from "~/bookings/views/bookings-listing-view";

export { getStaticProps } from "~/bookings/views/bookings-listing-view.getStaticProps";

const BookingsListingPage = new Proxy<{
  (): JSX.Element;
  PageWrapper?: typeof PageWrapper;
}>(BookingsListingView, {});

BookingsListingPage.PageWrapper = PageWrapper;

export const getStaticPaths: GetStaticPaths = () => {
  return {
    paths: validStatuses.map((status) => ({
      params: { status },
      locale: "en",
    })),
    fallback: "blocking",
  };
};

export default BookingsListingPage;

Pay attention to the imports at the top of this code snippet. Listing view component is imported from /bookings/views/bookings-listing-view

There's a dedicated components structure chapter in this guide to help you understand more about cal.com's component structures.

Bookings-listinig-view

You will find Bookings listing view component in modules folder

[Insert a screenshot of - https://github.com/calcom/cal.com/blob/main/apps/web/modules/bookings/views/bookings-listing-view.tsx]

At this point, it is important to ask yourself the right questions.

  1. How is the data fetched?
  2. Is there any state updated based on fetch response?
  3. Does the rendered JSX have any conditions inside the html code? if so, what are those conditions?

1. How is the data fetched?

The below code is responsible to fetch the data. cal.com uses tRPC to fetch the data. There's a dedicated chapter about API layer in this guide, let's focus on the state management.

 const query = trpc.viewer.bookings.get.useInfiniteQuery(
    {
      limit: 10,
      filters: {
        ...filterQuery,
        status: filterQuery.status ?? status,
      },
    },
    {
      enabled: true,
      getNextPageParam: (lastPage) => lastPage.nextCursor,
    }
  );

2. Is there any state updated based on fetch response?

There are no state variables updated based on the query but the query response is processed differently.

Example 1:

At Line 130 in bookings-listing-view you will find the below code:

const bookingsToday =
  query.data?.pages.map((page) =>
    page.bookings.filter((booking: BookingOutput) => {
      recurringInfoToday = page.recurringInfo.find(
        (info) => info.recurringEventId === booking.recurringEventId
      );

      return (
        dayjs(booking.startTime).tz(user?.timeZone).format("YYYY-MM-DD") ===
        dayjs().tz(user?.timeZone).format("YYYY-MM-DD")
      );
    })
)[0] || [];

bookingsToday here is updated based on query.data.

Example 2:

Similarly, isEmpty is computed based on query.data. Below code is picked from line 103

const isEmpty = !query.data?.pages[0]?.bookings.length;

You could make a tRPC call and process the response further to update variables with required response values, without using any setState methods. We can also observe that this component is on client side as it has "use client" at the top of the file. tRPC call did not happen inside a useEffect with an empty array dependency.

References:

  1. https://github.com/calcom/cal.com/tree/main/apps/web/pages/bookings