The grass is rarely greener, but it's always different

Basic memoizer function in Javascript

A basic user flow in TheGoodPsy is to allow patients to book appointments directly from the chat.

In order to do so, we have a Calendar component inside the application that wraps a FullCalendar component and extends its functionality with our custom logic.

We mainly use the day view where the patient has a glimpse of the slots available with their psychologist 24 hours from the current moment. This is to allow psychologists some room to maneuver and not have a Patient potentially booking an appointment within the next couple of hours they connect.

The FullCalendar component uses an events attribute that represents an event source of appointments to render. It can be an array, a json feed, or in our case, a function, fetchEvents().

By FullCalendar's behavior, the fetchEvents() function is called every time there is a view change, thus every time a patient clicks on the next/previous buttons to see different days, the function will be called. This function calls an external API to fetch those events according to a set of parameters, but we shouldn't need to ask for fresh values we've already received moments before.

So the simple solution is to use memoization to cache the results of the API calls using a combination of the psychologist id, start date (start of the day), and end date (end of the day). This way the call will only be done once per day in the calendar.

The memoizer function is simple:

const memoizer = (fn) => {
    let cachedResults = {}
    const internalFn = async (...args) => {
        const key = JSON.stringify(args)
        if (!cachedResults[key]) {
            cachedResults[key] = fn(...args);
        }
        return cachedResults[key]
    }
    const purge = () => cachedResults = Object.create({});
    return {
        internalFn,
        purge
    };
}

Using the memoizer() function we define getMemoizedAppointments() and a purgeCache() function to clean the cache eventually:

const getMemoizedAppointments = useCallback(memoizer(async (psId, start, end) => {
    if (psId && start && end) {
    //Fetch new events with the start/end parameter (Service, no dispatch)
        const newAppointments =
            await appointmentService.getAppointments({
                psyId: psId,
                start: start,
                end: end
            });
        return newAppointments;
    }
}), []);

const purgeCache = async () => await getMemoizedAppointments.purge();

An finally the fetchEvents() function:

const fetchEvents = async (fetchInfo, successCallback) => {
   //If the flag is set, we purge the cache and we refetch events.
   if (refetchCalendarEvents) {
      await purgeCache();
      handleRefetchCalendarEvents(false);
   }

   //Fetch new events with the start/end parameter (Service, no dispatch)
   const newAppointments =
      await getMemoizedAppointments.internalFn(
         psyId,
         moment(fetchInfo.start).toDate(),
         moment(fetchInfo.end).toDate()
      );

   setLocalAppointments(newAppointments);

   const newEvents =
      newAppointments ?
         mergeEventsOrderedByDate(
            newAppointments,
            mappedGoogleEvents
         ) :
      [];
  return successCallback(newEvents);
}

#javascript #learning #startup #web