10th November, 2024📖 4 min read

Syncing Data Across Browsers in Next.js: A Frontend 'Cron Job' Solution for Timed Fetching

Israel MichaelIsrael Michael
Syncing Data Across Browsers in Next.js: A Frontend 'Cron Job' Solution for Timed Fetching
Unknown source

Introduction

Imagine this: you’re building a dynamic Next.js app where data must refresh every two minutes. Easy, right? Just use setInterval in useEffect and call it a day! Well, that’s what I thought until I ran into an unexpected challenge. As I opened multiple browser tabs, I noticed each tab was fetching data independently, at different intervals. They weren’t in sync, and that wasn’t ideal for user experience.

This blog post is about the solution I arrived at: a frontend “cron job” approach to synchronize data-fetching intervals across multiple tabs. I’ll walk you through my process, the issues I faced, and the satisfying solution I found.

Starting Out: The Initial Timer-Based Fetch

At first, I set up my Next.js component to fetch data at a fixed interval. Here’s what it looked like initially:

"use client";

import { useEffect, useState } from "react";

export default function Page() {
  const [data, setData] = useState(null);

  const fetchData = async () => {
    const response = await fetch("/api/getData");
    const newData = await response.json();
    setData(newData);
  };

  useEffect(() => {
    fetchData();
    const interval = setInterval(fetchData, 120000); // Fetch every 2 minutes
    return () => clearInterval(interval);
  }, []);

  return <div>{data ? data : "Loading..."}</div>;
}

The Problem: Out-of-Sync Tabs

Everything seemed great, until I opened a second tab. With each tab running its own instance of setInterval, data fetching became unsynchronized. Tabs would refresh at different times, creating a disjointed user experience.

To illustrate, here’s how things looked:

  1. Opened the first tab at 12:01, so it fetches every odd minute (12:03, 12:05, etc.).
  2. Opened the second tab at 12:02, which then fetches every even minute (12:04, 12:06, etc.).

The issue became clear: I needed every tab to fetch at the same time, preferably at exact two-minute marks (like 12:00, 12:02, 12:04), regardless of when a user opened the tab.

Solution: Syncing Fetches at Even-Minute Marks

To achieve sync across multiple tabs, I decided to align all data fetches with even two-minute intervals (e.g., 12:00, 12:02, 12:04). This approach ensures all tabs are fetching data at the same time. Here’s how I tackled it:

  1. Calculate Time to the Next Even Minute: First, determine how long it is until the next two-minute mark. This will be the initial delay.
  2. Set a Timeout for the First Fetch: Once we know the time until the next even minute, use setTimeout to start the first fetch exactly at that point.
  3. Set a Consistent 2-Minute Interval Thereafter: After the initial fetch, set up a regular setInterval to continue fetching every two minutes.

This is how the final code turned out:

"use client";

import { useEffect, useState } from "react";

export default function Page() {
  const [data, setData] = useState(null);

  const fetchData = async () => {
    try {
      const response = await fetch("/api/getData");
      const newData = await response.json();
      setData(newData);
    } catch (err) {
      console.error("Error fetching data:", err);
    }
  };

  useEffect(() => {
    const now = new Date();
    const millisecondsUntilNextEvenMinute =
      120000 - ((now.getMinutes() % 2) * 60000 + now.getSeconds() * 1000);

    // Initial fetch at the next even 2-minute mark
    const initialFetchTimeout = setTimeout(() => {
      fetchData();
      // Set interval to fetch every 2 minutes after the initial sync
      const intervalId = setInterval(fetchData, 120000);
      return () => clearInterval(intervalId);
    }, millisecondsUntilNextEvenMinute);

    // Clear timeout on unmount
    return () => clearTimeout(initialFetchTimeout);
  }, []);

  return <div>{data ? data : "Loading..."}</div>;
}

Breaking Down the Code

  1. Calculate Initial Delay: By checking the current time in minutes and seconds, I computed the milliseconds remaining until the next two-minute interval.
  2. Start Fetching with a Timeout: The setTimeout uses the calculated delay to ensure that the first data fetch happens exactly at the next even minute.
  3. Set Interval After the First Fetch: Once the initial timeout triggers, setInterval starts the two-minute cycle, aligning all data fetches to happen in sync, every even minute across every open tab.

Result: Perfectly Synchronized Fetching

With this approach, I achieved a unified experience across all tabs. Now, no matter when a user opens the page, the data refreshes in sync at every two-minute mark. Here’s how it behaves now:

  • Open a tab at 12:01:29, it waits until 12:02:00, then fetches every two minutes thereafter.
  • Open another tab at 12:01:58, it waits only two seconds, then fetches at 12:02:00, staying in sync with other tabs.

Final Thoughts

This solution might feel like a small change, but it significantly improved the consistency of data display across tabs and browsers. This approach, aligning fetches to specific intervals, is sometimes called a “frontend cron job” because it emulates the regular scheduling of a backend cron job right in the browser.

When to Use This Approach

This sync method is useful when:

  • You’re working with time-sensitive data that should be consistent across tabs.
  • A traditional setInterval wouldn’t align with other instances in different tabs.