ahooks
Version:
react hooks library
108 lines (107 loc) • 4.27 kB
JavaScript
import { useEffect, useRef, useState } from 'react';
// {[path]: count}
// remove external when no used
const EXTERNAL_USED_COUNT = {};
const loadScript = (path, props = {}) => {
const script = document.querySelector(`script[src="${path}"]`);
if (!script) {
const newScript = document.createElement('script');
newScript.src = path;
Object.keys(props).forEach((key) => {
newScript[key] = props[key];
});
newScript.setAttribute('data-status', 'loading');
document.body.appendChild(newScript);
return {
ref: newScript,
status: 'loading',
};
}
return {
ref: script,
status: script.getAttribute('data-status') || 'ready',
};
};
const loadCss = (path, props = {}) => {
const css = document.querySelector(`link[href="${path}"]`);
if (!css) {
const newCss = document.createElement('link');
newCss.rel = 'stylesheet';
newCss.href = path;
Object.keys(props).forEach((key) => {
newCss[key] = props[key];
});
// IE9+
const isLegacyIECss = 'hideFocus' in newCss;
// use preload in IE Edge (to detect load errors)
if (isLegacyIECss && newCss.relList) {
newCss.rel = 'preload';
newCss.as = 'style';
}
newCss.setAttribute('data-status', 'loading');
document.head.appendChild(newCss);
return {
ref: newCss,
status: 'loading',
};
}
return {
ref: css,
status: css.getAttribute('data-status') || 'ready',
};
};
const useExternal = (path, options) => {
const [status, setStatus] = useState(path ? 'loading' : 'unset');
const ref = useRef(undefined);
useEffect(() => {
if (!path) {
setStatus('unset');
return;
}
const pathname = path.replace(/[|#].*$/, '');
if ((options === null || options === void 0 ? void 0 : options.type) === 'css' || (!(options === null || options === void 0 ? void 0 : options.type) && /(^css!|\.css$)/.test(pathname))) {
const result = loadCss(path, options === null || options === void 0 ? void 0 : options.css);
ref.current = result.ref;
setStatus(result.status);
}
else if ((options === null || options === void 0 ? void 0 : options.type) === 'js' || (!(options === null || options === void 0 ? void 0 : options.type) && /(^js!|\.js$)/.test(pathname))) {
const result = loadScript(path, options === null || options === void 0 ? void 0 : options.js);
ref.current = result.ref;
setStatus(result.status);
}
else {
// do nothing
console.error("Cannot infer the type of external resource, and please provide a type ('js' | 'css'). " +
'Refer to the https://ahooks.js.org/hooks/dom/use-external/#options');
}
if (!ref.current) {
return;
}
if (EXTERNAL_USED_COUNT[path] === undefined) {
EXTERNAL_USED_COUNT[path] = 1;
}
else {
EXTERNAL_USED_COUNT[path] += 1;
}
const handler = (event) => {
var _a;
const targetStatus = event.type === 'load' ? 'ready' : 'error';
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.setAttribute('data-status', targetStatus);
setStatus(targetStatus);
};
ref.current.addEventListener('load', handler);
ref.current.addEventListener('error', handler);
return () => {
var _a, _b, _c;
(_a = ref.current) === null || _a === void 0 ? void 0 : _a.removeEventListener('load', handler);
(_b = ref.current) === null || _b === void 0 ? void 0 : _b.removeEventListener('error', handler);
EXTERNAL_USED_COUNT[path] -= 1;
if (EXTERNAL_USED_COUNT[path] === 0 && !(options === null || options === void 0 ? void 0 : options.keepWhenUnused)) {
(_c = ref.current) === null || _c === void 0 ? void 0 : _c.remove();
}
ref.current = undefined;
};
}, [path]);
return status;
};
export default useExternal;