File indexing completed on 2025-01-26 05:28:00

0001 import { useState, useEffect, useRef } from 'react'
0002 import { cancelablePromise, shallowEqual } from '../utils'
0003 import useMergeState from './useMergeState'
0004 
0005 // Inspired by https://github.com/tkh44/holen
0006 const useFetch = (
0007   initialUrl,
0008   initialOptions = {},
0009   { onMount = true, onResponse = () => { } } = {},
0010 ) => {
0011   const [config, setConfig] = useState({
0012     url: initialUrl,
0013     options: initialOptions,
0014   })
0015   const { state, set } = useMergeState({
0016     loading: true,
0017     data: null,
0018     error: null,
0019   })
0020   const canFetchRef = useRef(onMount)
0021 
0022   useEffect(() => {
0023     const { url, options } = config
0024     if (!canFetchRef.current) {
0025       canFetchRef.current = true
0026       return
0027     }
0028     const cancelable = cancelablePromise(fetch(url, options))
0029     set({ loading: true })
0030     cancelable.promise
0031       .then(res => res.json())
0032       .then((newData) => {
0033         set(({ error }) => {
0034           onResponse(error, newData)
0035           return {
0036             data: newData,
0037             loading: false,
0038           }
0039         })
0040         return newData
0041       }).catch((e) => {
0042         set(({ data }) => {
0043           onResponse(e, data)
0044           return {
0045             error: e,
0046             loading: false,
0047           }
0048         })
0049         return e
0050       })
0051 
0052     return () => cancelable.cancel() // eslint-disable-line
0053   }, [config.url, config.options])
0054 
0055   const updateConfig = key => updater => setConfig((prev) => {
0056     const updated = typeof updater === 'function' ? updater(prev[key]) : updater
0057 
0058     // make sure not to re-fetch data when updated is shallow equal to prev[key]
0059     if (shallowEqual(updated, prev[key])) {
0060       return prev
0061     }
0062     return ({
0063       ...prev,
0064       [key]: updated,
0065     })
0066   })
0067   return {
0068     setUrl: updateConfig('url'),
0069     setOptions: updateConfig('options'),
0070     setData: updater => set(
0071       ({ data }) => (typeof updater === 'function'
0072         ? { data: updater(data) }
0073         : { data: updater }),
0074     ),
0075     loading: state.loading,
0076     data: state.data,
0077     error: state.error,
0078     fetch: (urlUpdater, optionsUpdater) => setConfig(prev => ({
0079       url: typeof urlUpdater === 'function' ? urlUpdater(prev.url) : (urlUpdater || prev.url),
0080       // change reference of options, so that we can re-fetch data when call fetch
0081       options: typeof optionsUpdater === 'function' ? optionsUpdater(prev.options) : { ...(optionsUpdater || prev.options) },
0082     })),
0083   }
0084 }
0085 
0086 export default useFetch