10th November, 2024•📖 4 min read
Syncing Data Across Browsers in Next.js: A Frontend 'Cron Job' Solution for Timed Fetching
Israel Michael
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:
- Opened the first tab at 12:01, so it fetches every odd minute (12:03, 12:05, etc.).
- 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:
- 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.
- Set a Timeout for the First Fetch: Once we know the time until the next even minute, use
setTimeoutto start the first fetch exactly at that point. - Set a Consistent 2-Minute Interval Thereafter: After the initial fetch, set up a regular
setIntervalto 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
- Calculate Initial Delay: By checking the current time in minutes and seconds, I computed the milliseconds remaining until the next two-minute interval.
- Start Fetching with a Timeout: The
setTimeoutuses the calculated delay to ensure that the first data fetch happens exactly at the next even minute. - Set Interval After the First Fetch: Once the initial timeout triggers,
setIntervalstarts 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
setIntervalwouldn’t align with other instances in different tabs.