-
Notifications
You must be signed in to change notification settings - Fork 394
Expand file tree
/
Copy pathload-script.tsx
More file actions
85 lines (72 loc) · 2.12 KB
/
load-script.tsx
File metadata and controls
85 lines (72 loc) · 2.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import {useState, useEffect} from 'react';
const SCRIPTS_LOADED: Record<string, Promise<boolean>> = {};
type LoadScriptOptions = {
module?: boolean;
in?: 'head' | 'body';
attributes?: Record<string, string>;
};
export function loadScript(
src: string,
options?: LoadScriptOptions,
): Promise<boolean> {
const isScriptLoaded = SCRIPTS_LOADED[src];
// eslint-disable-next-line @typescript-eslint/no-misused-promises
if (isScriptLoaded) {
return isScriptLoaded;
}
const promise = new Promise<boolean>((resolve, reject) => {
const script = document.createElement('script');
if (options?.module) {
script.type = 'module';
} else {
script.type = 'text/javascript';
}
script.src = src;
script.onload = (): void => {
resolve(true);
};
script.onerror = (): void => {
reject(false);
};
if (options?.in === 'head') {
document.head.appendChild(script);
} else {
document.body.appendChild(script);
}
const attributes = options?.attributes;
if (attributes) {
Object.keys(attributes).forEach((key) => {
script.setAttribute(key, attributes[key]);
});
}
});
SCRIPTS_LOADED[src] = promise;
return promise;
}
/**
* The `useLoadScript` hook loads an external script tag in the browser. It allows React components to lazy-load third-party dependencies.
* @publicDocs
*/
export type LoadScriptParams = Parameters<typeof loadScript>;
/**
* The `useLoadScript` hook loads an external script tag in the browser. It allows React components to lazy-load large third-party dependencies.
* @publicDocs
*/
export function useLoadScript(
url: LoadScriptParams[0],
options?: LoadScriptParams[1],
): ScriptState {
const [status, setStatus] = useState<ScriptState>('loading');
useEffect(
() => {
loadScript(url, options)
.then(() => setStatus('done'))
.catch(() => setStatus('error'));
},
// Ignore options changes since it won't trigger a new load.
// eslint-disable-next-line react-hooks/exhaustive-deps
[url],
);
return status;
}
type ScriptState = 'loading' | 'done' | 'error';