1- import { useCallback , useEffect } from "react" ;
2-
3- import {
4- getFromWindowCache ,
5- updateCachedScript ,
6- CachedScript ,
7- addScriptUpdater ,
8- removeScriptUpdater ,
9- } from "../scriptCache" ;
10-
11- const getNewScript = ( source : string ) : HTMLScriptElement => {
12- const newScript = document . createElement ( "script" ) ;
13- newScript . async = true ;
14- newScript . setAttribute ( "src" , source ) ;
15- return newScript ;
16- } ;
1+ import { useCallback , useEffect , useRef } from "react" ;
2+ import { waitForScript } from "../scriptloader-support" ;
173
184export interface ScriptLoaderConfiguration {
195 onSuccess : ( ) => void ;
@@ -25,105 +11,35 @@ export interface ScriptLoader {
2511 ( config : ScriptLoaderConfiguration ) : void ;
2612}
2713
28- interface CachedScriptUpdater extends ScriptLoader { }
29-
30- const useCachedScriptUpdater : CachedScriptUpdater = ( {
31- onSuccess,
32- onFailure,
33- source,
34- } ) => {
35- const updater = useCallback (
36- ( { loading, failed, failureEvent } : CachedScript ) => {
37- if ( ! loading && ! failed ) {
38- onSuccess ( ) ;
39- }
40- if ( ! loading && failed ) {
41- onFailure ( failureEvent ) ;
42- }
43- } ,
44- [ onSuccess , onFailure ]
45- ) ;
46-
47- useEffect ( ( ) => {
48- addScriptUpdater ( source , updater ) ;
49- return ( ) => removeScriptUpdater ( source , updater ) ;
50- } , [ updater , source ] ) ;
51-
52- useEffect ( ( ) => {
53- // run updater with already cached info
54- updater ( getFromWindowCache ( source ) ) ;
55- } , [ source , updater ] ) ;
56- } ;
57-
5814const useScriptLoader : ScriptLoader = ( config ) => {
59- const { source } = config ;
60- useCachedScriptUpdater ( config ) ;
61-
62- const setupListeners = useCallback (
63- ( scriptRef : HTMLScriptElement ) : ( ( ) => void ) => {
64- const removeListeners = ( ) => {
65- scriptRef . removeEventListener ( "load" , loadEvent ) ;
66- scriptRef . removeEventListener ( "error" , errorEvent ) ;
67- } ;
68-
69- const generateScriptEventListener = (
70- getResultingCachedScript : ( ev : Event ) => Partial < CachedScript >
71- ) => ( ev : Event ) => {
72- updateCachedScript ( source , getResultingCachedScript ( ev ) ) ;
73- removeListeners ( ) ;
74- } ;
75-
76- const loadEvent = generateScriptEventListener ( ( ) => ( {
77- loading : false ,
78- failed : false ,
79- } ) ) ;
80-
81- const errorEvent = generateScriptEventListener ( ( err : ErrorEvent ) => ( {
82- loading : false ,
83- failed : true ,
84- failureEvent : err ,
85- } ) ) ;
86-
87- scriptRef . addEventListener ( "load" , loadEvent ) ;
88- scriptRef . addEventListener ( "error" , errorEvent ) ;
89-
90- return removeListeners ;
15+ const {
16+ source,
17+ onSuccess,
18+ onFailure = ( ) => {
19+ //noop
9120 } ,
92- [ source ]
21+ } = config ;
22+ const isMounted = useRef ( true ) ;
23+ useEffect ( ( ) => ( ) => ( isMounted . current = false ) ) ;
24+ const successFunc = useCallback ( ( ) => isMounted . current && onSuccess ( ) , [
25+ onSuccess ,
26+ ] ) ;
27+ const errorFunc = useCallback (
28+ ( err : ErrorEvent ) => isMounted . current && onFailure ( err ) ,
29+ [ onFailure ]
9330 ) ;
9431
9532 useEffect ( ( ) => {
96- let scriptRef = document . querySelector < HTMLScriptElement > (
97- `script[src="${ source } "]`
98- ) ;
99- const scriptExists = Boolean ( scriptRef ) ;
100-
101- if ( scriptExists ) {
102- const cachedScriptInfo = getFromWindowCache ( source ) ;
103- if ( ! cachedScriptInfo . scriptCreated ) {
104- // if we did not create the script, assume it has loaded
105- updateCachedScript ( source , {
106- loading : false ,
107- failed : false ,
108- } ) ;
109- return ;
33+ const waitForSource = async ( ) => {
34+ try {
35+ await waitForScript ( source ) ;
36+ successFunc ( ) ;
37+ } catch ( err ) {
38+ errorFunc ( err as ErrorEvent ) ;
11039 }
111-
112- // if we are not loading, do nothing
113- if ( ! cachedScriptInfo . loading ) return ;
114-
115- // if we are loading and we did create the script, listen
116- return setupListeners ( scriptRef ) ;
117- }
118-
119- // if we did not create the script, create it
120- scriptRef = getNewScript ( source ) ;
121- updateCachedScript ( source , { scriptCreated : true } ) ;
122- const removeListeners = setupListeners ( scriptRef ) ;
123- document . body . appendChild ( scriptRef ) ;
124-
125- return removeListeners ;
126- } , [ source , setupListeners ] ) ;
40+ } ;
41+ void waitForSource ( ) ;
42+ } , [ source , successFunc , errorFunc ] ) ;
12743} ;
12844
12945export default useScriptLoader ;
0 commit comments