'use client'; import { useState, useEffect, useRef } from 'react'; import { useSearchParams } from 'next/navigation'; // ===== 공통 페이지네이션 기본값 ===== export const DEFAULT_PAGINATION = { currentPage: 1, lastPage: 1, perPage: 100, total: 0, }; export type PaginationMeta = { currentPage: number; lastPage: number; perPage: number; total: number; }; // ===== 반환 타입 ===== export interface UseAccountingListPageReturn { data: TData[]; pagination: PaginationMeta; isLoading: boolean; mode: string | null; } /** * 회계 목록 페이지 공통 훅 * * searchParams의 mode를 읽어서 skipModes가 아닌 경우 fetchFn을 호출합니다. * data, pagination, isLoading, mode를 반환합니다. * * @param fetchFn - 데이터를 가져오는 함수 (클로저로 params 전달) * @param options.skipModes - 데이터 로드를 건너뛸 mode 값 (기본: ['new']) * * @example * const { data, pagination, isLoading, mode } = useAccountingListPage( * () => getSales({ perPage: 100 }) * ); */ export function useAccountingListPage( fetchFn: () => Promise<{ success?: boolean; data: TData[]; pagination?: PaginationMeta }>, options?: { skipModes?: string[] } ): UseAccountingListPageReturn { const searchParams = useSearchParams(); const mode = searchParams.get('mode'); const skipModes = options?.skipModes ?? ['new']; const [data, setData] = useState([]); const [pagination, setPagination] = useState(DEFAULT_PAGINATION); const [isLoading, setIsLoading] = useState(true); // fetchFn을 ref로 보관하여 stale closure 방지 const fetchFnRef = useRef(fetchFn); fetchFnRef.current = fetchFn; useEffect(() => { if (mode && skipModes.includes(mode)) { setIsLoading(false); return; } setIsLoading(true); fetchFnRef.current() .then(result => { setData(result.data ?? []); if (result.pagination) { setPagination(result.pagination); } }) .finally(() => setIsLoading(false)); // mode 변경 시에만 재실행 }, [mode]); return { data, pagination, isLoading, mode }; }