/
Korablik
/
Developer
Обзор
Документация
Войти
/
Korablik
/
Developer
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/lib/hooks.ts
215 строк
5 KB
Korablevsky
fixFinal
26 мар 2024, 20:29
26 мар 2024, 20:29
ef55868
Код
Авторство
О чём код?
import { useQueries, useQuery } from '@tanstack/react-query' import React, { useContext, useEffect, useState } from 'react' import { BookmarksContext } from '../context/BookmarksContextProvider' import { BASE_API_URL } from './constants' import { JobItem, JobItemExpanded } from './types' import { handleError } from './utils' import { ActiveIdContext } from '../context/ActiveIdContextProvider' import { SearchTextContext } from '../context/searchTextContextProvider' import { JobItemsContext } from '../context/JobItemsContextProvider' type JobItemApiResponse = { public: boolean jobItem: JobItemExpanded } const fetchJobItem = async (id: number): Promise<JobItemApiResponse> => { const response = await fetch(`${BASE_API_URL}/${id}`) if (!response.ok) { const errorData = await response.json() throw new Error(errorData.description) } const data = await response.json() return data } export function useJobItem(id: number | null) { const { data, isInitialLoading } = useQuery( ['job-item', id], () => (id ? fetchJobItem(id) : null), { staleTime: 1000 * 60 * 60, refetchOnWindowFocus: false, retry: false, enabled: Boolean(id), onError: handleError, } ) return { jobItem: data?.jobItem, isLoading: isInitialLoading, } as const } export function useJobItems(ids: number[]) { const results = useQueries({ queries: ids.map(id => ({ queryKey: ['job-item', id], queryFn: () => fetchJobItem(id), staleTime: 1000 * 60 * 60, refetchOnWindowFocus: false, retry: false, enabled: Boolean(id), onError: handleError, })), }) const jobItems = results .map(result => result.data?.jobItem) // .filter(jobItem => jobItem !== undefined) .filter(jobItem => Boolean(jobItem)) as JobItemExpanded[] const isLoading = results.some(result => result.isLoading) return { jobItems, isLoading, } as const } // -------------- type JobItemsApiResponse = { public: boolean sorted: boolean jobItems: JobItem[] } const fetchJobItems = async ( searchText: string ): Promise<JobItemsApiResponse> => { const response = await fetch(`${BASE_API_URL}?search=${searchText}`) if (!response.ok) { const errorData = await response.json() throw new Error(errorData.description) } const data = await response.json() return data } export function useSearchQuery(searchText: string) { const { data, isInitialLoading } = useQuery( ['job-items', searchText], () => fetchJobItems(searchText), { staleTime: 1000 * 60 * 60, refetchOnWindowFocus: false, retry: false, enabled: Boolean(searchText), onError: handleError, } ) return { jobItems: data?.jobItems ?? [], isLoading: isInitialLoading, } as const } // -------------------------------- export function useDebounce<T>(value: T, delay = 500): T { const [debouncedValue, setDebouncedValue] = useState(value) useEffect(() => { const timerId = setTimeout(() => setDebouncedValue(value), delay) return () => clearTimeout(timerId) }, [value, delay]) return debouncedValue } export function useActiveId() { const [activeid, setActiveid] = useState<number | null>(null) useEffect(() => { const handleHashChange = () => { const id = +window.location.hash.slice(1) setActiveid(id) } handleHashChange() window.addEventListener('hashchange', handleHashChange) return () => window.removeEventListener('hashchange', handleHashChange) }, []) return activeid } export function useLocalStorage<T>( key: string, initialValue: T ): [T, React.Dispatch<React.SetStateAction<T>>] { const [value, setValue] = useState(() => JSON.parse(localStorage.getItem(key) || JSON.stringify(initialValue)) ) useEffect(() => { localStorage.setItem(key, JSON.stringify(value)) }, [value, key]) return [value, setValue] as const } export function useOnClickOutside( refs: React.RefObject<HTMLElement>[], handler: () => void ) { useEffect(() => { const handleClick = (e: MouseEvent) => { if (refs.every(ref => !ref.current?.contains(e.target as Node))) { handler() } } document.addEventListener('click', handleClick) return () => { document.removeEventListener('click', handleClick) } }, [refs, handler]) } // -------------------------------- export function useBookmarksContext() { const context = useContext(BookmarksContext) if (!context) { throw new Error( 'useBookmarks must be used within a BookmarksContextProvider' ) } return context } export function useActiveIdContext() { const context = useContext(ActiveIdContext) if (!context) { throw new Error( 'useActiveIdContext must be used within a useActiveIdContextProvider' ) } return context } export function useSearchTextContext() { const context = useContext(SearchTextContext) if (!context) { throw new Error( 'useSearchTextContext must be used within a useSearchTextContextProvider' ) } return context } export function useJobItemsContext() { const context = useContext(JobItemsContext) if (!context) { throw new Error( 'useJobItemsContextContext must be used within a useJobItemsContextContextProvider' ) } return context }