UNPKG

@resourge/react-fetch

Version:

[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

174 lines (169 loc) 5.96 kB
/** * react-fetch v1.41.3 * * Copyright (c) resourge. * * This source code is licensed under the MIT license found in the * LICENSE.md file in the root directory of this source tree. * * @license MIT */ import { useRef, useEffect } from 'react'; import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector'; import { isAbortedError, QueueKingSystem, LoadingService } from '@resourge/http-service'; import NotificationService from '../services/NotificationService'; import { useId } from '../utils/useIdShim'; import { useIsOnline } from './useIsOnline'; import { useOnFocusFetch } from './useOnFocusFetch'; import { useRefMemo } from './useRefMemo'; function useFetch(method, config = {}) { const controllers = useRef(new Set()); const isErrorUsedRef = useRef(false); const isLoadingUsedRef = useRef(false); const fetchRef = useRef(() => {}); const isOnline = useIsOnline(); const isFetchEffectWithData = 'initialState' in config; const isFetchEffect = isFetchEffectWithData || 'deps' in config; const _config = config; const { id = useId(), initialState } = _config; const currentDataRef = useRefMemo(() => ({ data: typeof initialState === 'function' ? initialState() : initialState, isLoading: _config.enable !== false && !_config.silent && (isFetchEffect || isFetchEffectWithData), error: null })); const setLoading = isLoading => { if (!_config.silent) { if (isLoadingUsedRef.current) { const shouldUpdate = isLoading && currentDataRef.current.isLoading !== isLoading; currentDataRef.current.isLoading = isLoading; if (shouldUpdate) { NotificationService.notifyById(id); } } else { LoadingService.setLoading(_config.loadingService, isLoading); } } if (!isLoading) { NotificationService.notifyAll(); } }; const setFetchState = data => { currentDataRef.current.data = data; _config.onDataChange && _config.onDataChange(data); NotificationService.notifyAll(); }; const noLoadingFetch = async (...args) => { try { var _NotificationService$; const data = await ((_NotificationService$ = NotificationService.getRequest(id)) != null ? _NotificationService$ : (() => { const remove = QueueKingSystem.add(controller => controllers.current.add(controller), controller => controllers.current.delete(controller)); const prom = method.call(currentDataRef.current, ...(args != null ? args : [])).finally(() => { remove(); NotificationService.finishRequest(id); }); NotificationService.startRequest(id, prom); return prom; })()); if (isFetchEffect && isFetchEffectWithData) { currentDataRef.current.data = data; _config.onDataChange && _config.onDataChange(data); } return data; } catch (e) { if (isErrorUsedRef.current && !isAbortedError(e)) { currentDataRef.current.error = e; } return await Promise.reject(e); } }; const fetch = async (...args) => { if (!isOnline) { return await Promise.reject(new Error('No internet')); } if (isErrorUsedRef.current && currentDataRef.current.error) { currentDataRef.current.error = null; } setLoading(true); try { return await noLoadingFetch(...args); } finally { setLoading(false); } }; const { current: { subscribe, getSnapshot, selector, isEqual } } = useRefMemo(() => ({ subscribe: NotificationService.subscribe(id, () => fetchRef.current()), getSnapshot: () => ({ data: currentDataRef.current.data, isLoading: currentDataRef.current.isLoading, error: currentDataRef.current.error }), selector: selection => selection, isEqual: (previousState, newState) => previousState.isLoading === newState.isLoading && previousState.data === newState.data && previousState.error === newState.error })); useSyncExternalStoreWithSelector(subscribe, getSnapshot, getSnapshot, selector, isEqual); const result = { get isLoading() { isLoadingUsedRef.current = true; return currentDataRef.current.isLoading; }, get error() { isErrorUsedRef.current = true; return currentDataRef.current.error; }, fetch }; if (isFetchEffect) { var _config$deps; fetchRef.current = fetch; useEffect(() => { if (_config.enable !== false && isOnline) { QueueKingSystem.isThresholdEnabled = true; result.fetch().finally(() => { if (_config.scrollRestoration) { (Array.isArray(_config.scrollRestoration) ? _config.scrollRestoration : [_config.scrollRestoration]).forEach(method => { method == null || method(); }); } _config.onEffectEnd == null || _config.onEffectEnd(); }); } }, [isOnline, _config.enable, ...((_config$deps = _config.deps) != null ? _config$deps : [])]); if (isFetchEffectWithData) { var _config$onWindowFocus; useOnFocusFetch(async () => { if (isErrorUsedRef.current && currentDataRef.current.error) { currentDataRef.current.error = null; NotificationService.notifyById(id); } await noLoadingFetch(); NotificationService.notifyAll(); }, _config.enable !== false ? (_config$onWindowFocus = _config.onWindowFocus) != null ? _config$onWindowFocus : true : false); result.data = currentDataRef.current.data; result.setFetchState = setFetchState; } } useEffect(() => { return () => { NotificationService.finishRequest(id); if (controllers.current.size) { controllers.current.forEach(controller => { controller.abort(); }); controllers.current.clear(); } }; }, []); return result; } export { useFetch }; //# sourceMappingURL=useFetch.js.map