diff --git a/src/components/address-page.tsx b/src/components/address-page.tsx index 996f8bd..efad36e 100644 --- a/src/components/address-page.tsx +++ b/src/components/address-page.tsx @@ -1,8 +1,10 @@ "use client"; +import { useEffect, useState } from "react"; import dynamic from "next/dynamic"; import type { City } from "@/lib/types"; import { useAddressState } from "@/hooks/use-address-state"; +import { useMediaQuery } from "@/hooks/use-media-query"; import { AddressForm } from "@/components/address-form"; const TaiwanMap = dynamic( @@ -15,25 +17,57 @@ const TaiwanMap = dynamic( }, ); +const MapPlaceholder = () => ( +
+); + interface AddressPageProps { cities: City[]; } export function AddressPage({ cities }: AddressPageProps) { const addressState = useAddressState(); + const isDesktop = useMediaQuery("(min-width: 1024px)"); + const [mapReady, setMapReady] = useState(false); + + useEffect(() => { + if (!isDesktop) return; + + if (typeof requestIdleCallback === "function") { + const id = requestIdleCallback(() => { + setMapReady(true); + }); + return () => { + cancelIdleCallback(id); + }; + } + + const id = setTimeout(() => { + setMapReady(true); + }, 0); + return () => { + clearTimeout(id); + }; + }, [isDesktop]); return (
-
-
- + {isDesktop && ( +
+
+ {mapReady ? ( + + ) : ( + + )} +
-
+ )}
); } diff --git a/src/hooks/use-media-query.ts b/src/hooks/use-media-query.ts new file mode 100644 index 0000000..09bdcbe --- /dev/null +++ b/src/hooks/use-media-query.ts @@ -0,0 +1,18 @@ +import { useCallback, useSyncExternalStore } from "react"; + +export function useMediaQuery(query: string): boolean { + const subscribe = useCallback( + (callback: () => void) => { + const mql = window.matchMedia(query); + mql.addEventListener("change", callback); + return () => { + mql.removeEventListener("change", callback); + }; + }, + [query], + ); + + const getSnapshot = () => window.matchMedia(query).matches; + + return useSyncExternalStore(subscribe, getSnapshot, () => false); +}