@resourge/react-fetch
Version:
[](LICENSE)
174 lines (169 loc) • 5.96 kB
JavaScript
/**
* 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