Files
payload/packages/ui/src/hooks/usePayloadAPI.ts
Alessio Gravili 6c99326d59 feat: replace qs with qs-esm (#6966)
qs-esm is a qs fork I created and doesn't add bloated polyfills, is
ESM-only, has a smaller bundle size and comes with types included.

qs:
https://bundlephobia.com/package/qs@6.12.1 (11kb)
https://npm.anvaka.com/#/view/2d/qs (15 dependencies)

qs-esm:
https://bundlephobia.com/package/qs-esm@7.0.0 (4.2kb)
https://npm.anvaka.com/#/view/2d/qs-esm (1 dependency)

I don't agree with the backwards philosophy of qs:
https://github.com/ljharb/qs/issues/404#issuecomment-806392831 ("more
deps is better", lower bundle size as opt-in, maximum environment
compatibility as opt-out)

qs imports waaay too many useless dependencies
2024-07-09 14:33:38 +00:00

105 lines
2.4 KiB
TypeScript

'use client'
import * as qs from 'qs-esm'
import { useEffect, useRef, useState } from 'react'
import { useLocale } from '../providers/Locale/index.js'
import { useTranslation } from '../providers/Translation/index.js'
import { requests } from '../utilities/api.js'
type Result = [
{
data: any
isError: boolean
isLoading: boolean
},
{
setParams: React.Dispatch<unknown>
},
]
type Options = {
initialData?: any
initialParams?: unknown
}
type UsePayloadAPI = (url: string, options?: Options) => Result
export const usePayloadAPI: UsePayloadAPI = (url, options = {}) => {
const { initialData, initialParams = {} } = options
const { i18n } = useTranslation()
const [data, setData] = useState(initialData || {})
const [params, setParams] = useState(initialParams)
const [isLoading, setIsLoading] = useState(!initialData)
const [isError, setIsError] = useState(false)
const { code: locale } = useLocale()
const hasInitialized = useRef(false)
const search = qs.stringify(
{
locale,
...(typeof params === 'object' ? params : {}),
},
{
addQueryPrefix: true,
},
)
// If `initialData`, no need to make a request
useEffect(() => {
if (initialData && !hasInitialized.current) {
hasInitialized.current = true
return
}
const abortController = new AbortController()
const fetchData = async () => {
setIsError(false)
setIsLoading(true)
try {
const response = await requests.get(`${url}${search}`, {
headers: {
'Accept-Language': i18n.language,
},
signal: abortController.signal,
})
if (response.status > 201) {
setIsError(true)
}
const json = await response.json()
setData(json)
setIsLoading(false)
} catch (error) {
if (!abortController.signal.aborted) {
setIsError(true)
setIsLoading(false)
}
}
}
if (url) {
void fetchData()
} else {
setIsError(false)
setIsLoading(false)
}
return () => {
abortController.abort()
}
}, [url, locale, search, i18n.language, initialData])
// If `initialData` changes, reset the state
useEffect(() => {
if (initialData && hasInitialized.current) {
setData(initialData)
}
}, [initialData])
return [{ data, isError, isLoading }, { setParams }]
}