Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 26x 26x 2x 2x 23x 23x 1x 1x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x 7x 7x 7x 7x 22x 22x 22x 4x 4x 4x 4x 4x 4x 4x 4x 4x 22x 22x 22x 22x 22x 22x 22x 22x | "use client"
import React, { createContext, useContext, useState, useCallback, useMemo } from "react";
import { useSearchParams, usePathname, useRouter } from "next/navigation";
interface ModalContextType {
isModalOpen: boolean;
openModal: (productId?: number) => void;
closeModal: () => void;
previewProductId: number | null;
}
const ModalContext = createContext<ModalContextType | undefined>(undefined);
export const useModalContext = () => {
const context = useContext(ModalContext);
if (!context) {
throw new Error("useModalContext must be used within a ModalProvider");
}
return context;
};
export const ModalProvider = ({ children }: { children: React.ReactNode }) => {
// Track manual open/close state separately from URL-derived state
const [manualModalState, setManualModalState] = useState<{ isOpen: boolean; productId: number | null } | null>(null);
const searchParams = useSearchParams();
const pathname = usePathname();
const router = useRouter();
// Derive modal state from URL params - this avoids setState in effect
const urlDerivedState = useMemo(() => {
const previewParam = searchParams.get('preview');
if (previewParam) {
const productId = parseInt(previewParam, 10);
if (!isNaN(productId)) {
return { isOpen: true, productId };
}
}
return { isOpen: false, productId: null };
}, [searchParams]);
// Combine manual state with URL-derived state (manual takes precedence when set)
const isModalOpen = manualModalState?.isOpen ?? urlDerivedState.isOpen;
const previewProductId = manualModalState?.productId ?? urlDerivedState.productId;
const openModal = useCallback((productId?: number) => {
// Update URL with preview param if productId is provided
if (productId && typeof window !== 'undefined') {
const params = new URLSearchParams(searchParams.toString());
params.set('preview', productId.toString());
router.replace(`${pathname}?${params.toString()}`, { scroll: false });
}
// Set manual state for immediate UI update
setManualModalState({ isOpen: true, productId: productId ?? null });
}, [searchParams, pathname, router]);
const closeModal = useCallback(() => {
// Remove preview param from URL
if (typeof window !== 'undefined') {
const params = new URLSearchParams(searchParams.toString());
params.delete('preview');
const newUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname;
router.replace(newUrl, { scroll: false });
}
// Set manual state for immediate UI update
setManualModalState({ isOpen: false, productId: null });
}, [searchParams, pathname, router]);
return (
<ModalContext.Provider value={{ isModalOpen, openModal, closeModal, previewProductId }}>
{children}
</ModalContext.Provider>
);
}; |