/** * Custom Hook Template * * Usage: Copy this file to create new custom hooks * 1. Replace TEMPLATE_NAME with your hook name (use camelCase) * 2. Define the hook parameters and return type * 3. Implement your hook logic */ 'use client' import { useState, useEffect, useCallback, useRef } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' // Hook parameters interface interface UseTemplateHookParams { // Define your parameters here initialValue?: string autoFetch?: boolean onSuccess?: (data: any) => void onError?: (error: Error) => void debounceMs?: number } // Hook return type interface UseTemplateHookReturn { // Define what your hook returns data: any isLoading: boolean error: Error | null refetch: () => void mutate: (data: any) => void reset: () => void } /** * Custom hook template * * @param params - Hook parameters * @returns Hook state and functions */ export function useTemplateHook({ initialValue = '', autoFetch = true, onSuccess, onError, debounceMs = 300, }: UseTemplateHookParams = {}): UseTemplateHookReturn { // Local state const [data, setData] = useState(initialValue) const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState(null) // Refs for cleanup and debouncing const timeoutRef = useRef(undefined) const queryClient = useQueryClient() // Query for fetching data const query = useQuery({ queryKey: ['template-hook', initialValue], queryFn: async () => { // Implement your data fetching logic const response = await fetch('/api/template-endpoint', { method: 'GET', headers: { 'Content-Type': 'application/json', }, }) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } return response.json() }, enabled: autoFetch, }) // Handle query success/error with useEffect useEffect(() => { if (query.data && !query.isLoading && !query.error) { setData(query.data) onSuccess?.(query.data) } }, [query.data, query.isLoading, query.error, onSuccess]) useEffect(() => { if (query.error) { setError(query.error) onError?.(query.error) } }, [query.error, onError]) // Mutation for updating data const mutation = useMutation({ mutationFn: async (newData: any) => { const response = await fetch('/api/template-endpoint', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(newData), }) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } return response.json() }, }) // Handle mutation success/error with useEffect useEffect(() => { if (mutation.data && mutation.isSuccess) { // Update cache queryClient.setQueryData(['template-hook', initialValue], mutation.data) setData(mutation.data) onSuccess?.(mutation.data) } }, [mutation.data, mutation.isSuccess, queryClient, initialValue, onSuccess]) useEffect(() => { if (mutation.error) { setError(mutation.error) onError?.(mutation.error) } }, [mutation.error, onError]) // Debounced function const debouncedFunction = useCallback( (value: any) => { if (timeoutRef.current) { clearTimeout(timeoutRef.current) } timeoutRef.current = setTimeout(() => { // Implement debounced logic here console.log('Debounced value:', value) }, debounceMs) }, [debounceMs] ) // Effect for cleanup useEffect(() => { return () => { if (timeoutRef.current) { clearTimeout(timeoutRef.current) } } }, []) // Refetch function const refetch = useCallback(() => { query.refetch() }, [query]) // Mutate function const mutate = useCallback( (newData: any) => { mutation.mutate(newData) }, [mutation] ) // Reset function const reset = useCallback(() => { setData(initialValue) setError(null) queryClient.removeQueries({ queryKey: ['template-hook', initialValue] }) }, [initialValue, queryClient]) return { data: query.data || data, isLoading: query.isLoading || mutation.isPending, error: query.error || mutation.error || error, refetch, mutate, reset, } } /** * Alternative hook patterns: * * 1. Simple State Hook: * * export function useSimpleState(initialValue: T) { * const [value, setValue] = useState(initialValue); * * const reset = useCallback(() => setValue(initialValue), [initialValue]); * * return [value, setValue, reset] as const; * } * * 2. Local Storage Hook: * * export function useLocalStorage(key: string, initialValue: T) { * const [storedValue, setStoredValue] = useState(() => { * try { * const item = window.localStorage.getItem(key); * return item ? JSON.parse(item) : initialValue; * } catch (error) { * return initialValue; * } * }); * * const setValue = (value: T | ((val: T) => T)) => { * try { * const valueToStore = value instanceof Function ? value(storedValue) : value; * setStoredValue(valueToStore); * window.localStorage.setItem(key, JSON.stringify(valueToStore)); * } catch (error) { * console.error('Error saving to localStorage:', error); * } * }; * * return [storedValue, setValue] as const; * } * * 3. API Hook with SWR pattern: * * export function useApi(url: string, options?: RequestInit) { * const [data, setData] = useState(null); * const [loading, setLoading] = useState(true); * const [error, setError] = useState(null); * * useEffect(() => { * let cancelled = false; * * const fetchData = async () => { * try { * setLoading(true); * const response = await fetch(url, options); * * if (!response.ok) { * throw new Error(`HTTP error! status: ${response.status}`); * } * * const result = await response.json(); * * if (!cancelled) { * setData(result); * setError(null); * } * } catch (err) { * if (!cancelled) { * setError(err instanceof Error ? err : new Error('Unknown error')); * } * } finally { * if (!cancelled) { * setLoading(false); * } * } * }; * * fetchData(); * * return () => { * cancelled = true; * }; * }, [url]); * * return { data, loading, error }; * } */