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