import { useState, useCallback, useEffect, ReactNode, createElement, Fragment } from 'react';
import { useSearchParams } from 'next/navigation';
function parseLocalDate(dateStr: string): Date | null {
const [year, month, day] = dateStr.split('-');
if (!year || !month || !day) return null;
return new Date(Number(year), Number(month) - 1, Number(day), 12, 0, 0);
}
function formatLocalDate(date: Date): string {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
}
/**
* Hook that handles date synchronization with URL parameters
* Should only be used within a component wrapped in Suspense
*/
export function useSyncedDateWithParams() {
const searchParams = useSearchParams();
// Initialize with null, then set in useEffect to ensure client-side only
const [date, setDate] = useState<Date | null>(null);
// Get date from search params
useEffect(() => {
const dateStr = searchParams?.get('date') || null;
const parsedDate = dateStr ? parseLocalDate(dateStr) : null;
const validDate = parsedDate && !isNaN(parsedDate.getTime()) ? parsedDate : new Date();
setDate(validDate);
}, [searchParams]);
const handleDateChange = useCallback((newDate: Date | null) => {
setDate(newDate);
// Update URL
if (typeof window !== 'undefined') {
const params = new URLSearchParams(window.location.search);
if (newDate) {
params.set('date', formatLocalDate(newDate));
} else {
params.delete('date');
}
window.history.replaceState({}, '', `${window.location.pathname}?${params}`);
}
}, []);
return { date, handleDateChange };
}
/**
* Client component that safely uses useSearchParams
* Must be wrapped in a Suspense boundary
*/
export function DateParamHandler({ children }: {
children: (props: { date: Date | null; handleDateChange: (newDate: Date | null) => void }) => ReactNode
}) {
const { date, handleDateChange } = useSyncedDateWithParams();
// Use React.createElement to avoid JSX syntax issues
return createElement(Fragment, null, children({ date, handleDateChange }));
}
/**
* @deprecated Use DateParamHandler with Suspense instead
*/
export function useSyncedDate() {
const searchParams = useSearchParams();
// Initialize with null, then set in useEffect to ensure client-side only
const [date, setDate] = useState<Date | null>(null);
// Get date from search params
useEffect(() => {
const dateStr = searchParams?.get('date') || null;
const parsedDate = dateStr ? parseLocalDate(dateStr) : null;
const validDate = parsedDate && !isNaN(parsedDate.getTime()) ? parsedDate : new Date();
setDate(validDate);
}, [searchParams]);
const handleDateChange = useCallback((newDate: Date | null) => {
setDate(newDate);
// Update URL
if (typeof window !== 'undefined') {
const params = new URLSearchParams(window.location.search);
if (newDate) {
params.set('date', formatLocalDate(newDate));
} else {
params.delete('date');
}
window.history.replaceState({}, '', `${window.location.pathname}?${params}`);
}
}, []);
return [date, handleDateChange] as const;
}