Skip to main content

Schedule use cases

import { useScheduledTimer } from '@crup/react-timer-hook/schedules';

Use schedules when a timer needs side effects on a cadence. updateIntervalMs is the fallback UI cadence. If a schedule is faster, the scheduler wakes at the schedule cadence so callbacks receive timely scheduledAt and firedAt context.

Poll backend status

const timer = useScheduledTimer({
autoStart: true,
updateIntervalMs: 1000,
endWhen: snapshot => snapshot.now >= expiresAt,
schedules: [
{
id: 'auction-status',
everyMs: 5000,
overlap: 'skip',
callback: async (_snapshot, controls) => {
const auction = await api.getAuction(auctionId);
if (auction.status === 'sold') controls.cancel('sold');
},
},
],
});

Use this for auction status checks, job status polling, order payment status, device heartbeat checks, and reservation validation.

Autosave heartbeat

const timer = useScheduledTimer({
autoStart: true,
schedules: [
{
id: 'autosave',
everyMs: 5000,
overlap: 'skip',
callback: () => saveDraft(),
onError: error => reportAutosaveError(error),
},
],
});

Use this for draft autosave, presence pings, collaborative editor heartbeats, and "last active" updates.

Use timing context

callback: async (_snapshot, _controls, context) => {
metrics.timing('poll.delay', context.firedAt - context.scheduledAt);
metrics.count('poll.overdue', context.overdueCount);
}

overdueCount tells you how many intervals were missed because the browser, JS thread, or callback work was late.

Stop the lifecycle from a schedule

schedules: [
{
id: 'payment-status',
everyMs: 3000,
callback: async (_snapshot, controls) => {
const payment = await api.getPayment(paymentId);
if (payment.status === 'paid') controls.cancel('paid');
if (payment.status === 'failed') controls.cancel('failed');
},
},
]

cancel(reason) is for terminal early stops. It does not call onEnd.

For live demos, see polling schedule, autosave heartbeat, poll and cancel, and backend event stop.