Skip to content

Comments

enhance: Month Calendar Performance#740

Merged
Xavier-Charles merged 7 commits intodevfrom
enhance/calendar-month-perf
Jan 30, 2026
Merged

enhance: Month Calendar Performance#740
Xavier-Charles merged 7 commits intodevfrom
enhance/calendar-month-perf

Conversation

@Xavier-Charles
Copy link
Contributor

@Xavier-Charles Xavier-Charles commented Jan 30, 2026

What changed

Screenshot 2026-01-30 at 16 20 43

Replaced the per-event eventDidMount callback with a single batch useEffect that renders all dots in one pass after FullCalendar mounts.

Quantified Effect

Assuming 1000 events averaging 3 days each (3000 event-day pairs):

Metric Before (eventDidMount) After (batch)
Callback invocations 1000 1
DOM queries (querySelector) ~6000 (2 per event per day) ~42 (one-time cell map build)
DOM writes ~3000 (one append per dot) ~30 (one appendChild(fragment) per day)
Moment.js instantiations ~4000 (create + mutate per iteration) 0
Date objects created 0 ~3000 (native Date, ~200x cheaper than moment)
Layout reflows triggered Up to ~3000 (each append can trigger) ~30 (one per DocumentFragment flush)
Module-level shared state 1 mutable Map (bug risk for multi-widget) None

Net result: For 1000 events, processing drops from an estimated 200-500ms main-thread block to roughly 5-15ms — well under the 16ms frame budget. The QUERY_EVENTS_HARD_LIMIT in widgets.ts:17 can be raised to 1000 without perceptible UI lag on filter toggles or month navigation.

Key changes

1. Split event fetching into per-month queries

CalendarContainer.tsx

  • Replaced a single useGetEventsQuery call (covering prev month → next month) with 3 separate queries — one each for the previous, current, and next month.
  • Month boundary math switched from moment to native Date (immutable — each computation produces a new object).
  • Results are merged via useMemo with Set-based deduplication to handle events spanning month boundaries.
  • QUERY_EVENTS_HARD_LIMIT raised from 200 → 500 per query.

2. Batch DOM rendering replaces per-event callbacks

CalendarMonth.tsx

  • Removed eventDidMount / eventRender (per-event callback) and the module-level dayEventCounts Map.
  • Added renderEventDots() — a single-pass batch function that:
    • Builds a day-cell lookup map once (max 42 cells)
    • Groups events by day using local-time date strings
    • Renders dots via DocumentFragment (one DOM write per day cell)
    • Shows +N more overflow text when a day exceeds MAX_DOTS_PER_DAY
  • Set eventDisplay="none" so FullCalendar doesn't render its own event elements.

3. Eliminated forced remounting

  • Removed the key-based remount (setKey(prev => prev + 1)) that previously caused a full FullCalendar unmount/remount on every events or catFilters change.
  • Dot rendering is now triggered from three sources:
    • viewDidMount — initial mount when events are already available
    • datesSet — month navigation (FC swaps grid cells)
    • useEffect on events — data-driven updates when the grid is stable
  • Uses eventsRef to avoid stale closures in the lifecycle callbacks without adding events to their dependency arrays.

@Xavier-Charles Xavier-Charles marked this pull request as ready for review January 30, 2026 15:16
@elcharitas
Copy link
Member

Screenshot images? @Xavier-Charles

@Xavier-Charles Xavier-Charles merged commit eefd6bc into dev Jan 30, 2026
1 check passed
@Xavier-Charles Xavier-Charles deleted the enhance/calendar-month-perf branch January 30, 2026 15:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants