@@ -3,11 +3,15 @@ import { isBrowserEnvironment } from "../constants/is-browser-environment";
3
3
import { UnityLoaderStatus } from "../enums/unity-loader-status" ;
4
4
import { UnityConfig } from "../exports" ;
5
5
6
- // Map to track script references and their instance count
7
- const scriptReferenceMap = new Map <
8
- string ,
9
- { count : number ; status : UnityLoaderStatus }
10
- > ( ) ;
6
+ interface ScriptData {
7
+ count : number ;
8
+ status : UnityLoaderStatus ;
9
+ handleLoad ?: ( ) => void ;
10
+ handleError ?: ( ) => void ;
11
+ }
12
+
13
+ // Map to track script references, status, and event handlers.
14
+ const scriptReferenceMap = new Map < string , ScriptData > ( ) ;
11
15
12
16
/**
13
17
* Hook to embed a Unity Loader script.
@@ -25,86 +29,111 @@ const useUnityLoader = (unityConfig: UnityConfig): UnityLoaderStatus => {
25
29
// It is possible for the application being rendered server side. In
26
30
// this scenario, the window is not available. We can't create a Unity
27
31
// Loader in this case.
28
- if ( isBrowserEnvironment === false ) {
32
+ if ( ! isBrowserEnvironment ) {
29
33
return undefined ;
30
34
}
35
+
31
36
// If the script's source is null, we'll reset the status to idle.
32
- if ( unityConfig . loaderUrl === null ) {
37
+ if ( ! loaderUrl ) {
33
38
setStatus ( UnityLoaderStatus . Idle ) ;
34
39
return undefined ;
35
40
}
36
41
37
- /**
38
- * Find existing script element by source. It may have been added by
39
- * another instance of this hook.
40
- */
42
+ // Find existing script element by source.
43
+ // It may have been added by another instance of this hook.
41
44
let script = document . querySelector (
42
45
`script[src="${ loaderUrl } "]`
43
46
) as HTMLScriptElement | null ;
44
47
45
- // If script exists, increase reference count, else we'll create a new script.
48
+ // Get existing data from the reference map.
49
+ const existingData = scriptReferenceMap . get ( loaderUrl ) ;
50
+
46
51
if ( script ) {
47
- const refData = scriptReferenceMap . get ( loaderUrl ) || {
48
- count : 0 ,
49
- status : UnityLoaderStatus . Loading ,
50
- } ;
51
- scriptReferenceMap . set ( loaderUrl , {
52
- count : refData . count + 1 ,
53
- status : refData . status ,
54
- } ) ;
55
- setStatus ( refData . status ) ;
52
+ // If the script is already in the DOM, increment the ref count.
53
+ if ( existingData ) {
54
+ existingData . count += 1 ;
55
+ scriptReferenceMap . set ( loaderUrl , existingData ) ;
56
+ setStatus ( existingData . status ) ;
57
+ } else {
58
+ // Edge case: script is in the DOM, but not in the map.
59
+ // (Unlikely if all usage is through this hook)
60
+ scriptReferenceMap . set ( loaderUrl , {
61
+ count : 1 ,
62
+ status : UnityLoaderStatus . Loaded ,
63
+ } ) ;
64
+ setStatus ( UnityLoaderStatus . Loaded ) ;
65
+ }
56
66
} else {
67
+ // Create a new script element.
57
68
script = document . createElement ( "script" ) ;
58
69
script . type = "text/javascript" ;
59
70
script . src = loaderUrl ;
60
71
script . async = true ;
61
72
script . setAttribute ( "data-status" , "loading" ) ;
62
73
document . body . appendChild ( script ) ;
63
74
64
- // Initialize reference map entry
65
- scriptReferenceMap . set ( loaderUrl , {
66
- count : 1 ,
67
- status : UnityLoaderStatus . Loading ,
68
- } ) ;
69
-
70
- script . addEventListener ( "load" , ( ) => {
75
+ // Define load handler.
76
+ const handleLoad = ( ) => {
71
77
script ?. setAttribute ( "data-status" , "loaded" ) ;
72
- scriptReferenceMap . set ( loaderUrl , {
73
- count : 1 ,
74
- status : UnityLoaderStatus . Loaded ,
75
- } ) ;
78
+ const refData = scriptReferenceMap . get ( loaderUrl ) ;
79
+ if ( refData ) {
80
+ refData . status = UnityLoaderStatus . Loaded ;
81
+ scriptReferenceMap . set ( loaderUrl , refData ) ;
82
+ }
76
83
setStatus ( UnityLoaderStatus . Loaded ) ;
77
- } ) ;
84
+ } ;
78
85
79
- script . addEventListener ( "error" , ( ) => {
86
+ // Define error handler.
87
+ const handleError = ( ) => {
80
88
script ?. setAttribute ( "data-status" , "error" ) ;
81
- scriptReferenceMap . set ( loaderUrl , {
82
- count : 1 ,
83
- status : UnityLoaderStatus . Error ,
84
- } ) ;
89
+ const refData = scriptReferenceMap . get ( loaderUrl ) ;
90
+ if ( refData ) {
91
+ refData . status = UnityLoaderStatus . Error ;
92
+ scriptReferenceMap . set ( loaderUrl , refData ) ;
93
+ }
85
94
setStatus ( UnityLoaderStatus . Error ) ;
95
+ } ;
96
+
97
+ // Attach listeners.
98
+ script . addEventListener ( "load" , handleLoad ) ;
99
+ script . addEventListener ( "error" , handleError ) ;
100
+
101
+ // Initialize the reference map.
102
+ scriptReferenceMap . set ( loaderUrl , {
103
+ count : 1 ,
104
+ status : UnityLoaderStatus . Loading ,
105
+ handleLoad,
106
+ handleError,
86
107
} ) ;
108
+
109
+ setStatus ( UnityLoaderStatus . Loading ) ;
87
110
}
88
111
89
- // Remove event listeners on cleanup.
90
112
return ( ) => {
91
113
const refData = scriptReferenceMap . get ( loaderUrl ) ;
114
+ if ( ! refData ) return ;
115
+
116
+ // Decrement the ref count.
117
+ refData . count -= 1 ;
92
118
93
- if ( refData ) {
94
- if ( refData . count > 1 ) {
95
- // Decrease reference count when an instance unmounts
96
- scriptReferenceMap . set ( loaderUrl , {
97
- ...refData ,
98
- count : refData . count - 1 ,
99
- } ) ;
100
- } else {
101
- // Remove script only when the last instance unmounts
102
- scriptReferenceMap . delete ( loaderUrl ) ;
103
- script ?. remove ( ) ;
119
+ // If there are no more consumers of the script, remove it from the DOM.
120
+ if ( refData . count <= 0 ) {
121
+ scriptReferenceMap . delete ( loaderUrl ) ;
122
+
123
+ // Also remove listeners before removing the script.
124
+ if ( script && refData . handleLoad && refData . handleError ) {
125
+ script . removeEventListener ( "load" , refData . handleLoad ) ;
126
+ script . removeEventListener ( "error" , refData . handleError ) ;
104
127
}
128
+
129
+ script ?. remove ( ) ;
130
+ } else {
131
+ // If there's still at least one consumer of the script, update the map
132
+ // but don't remove the script from the DOM.
133
+ scriptReferenceMap . set ( loaderUrl , refData ) ;
105
134
}
106
135
} ;
107
- } , [ unityConfig . loaderUrl ] ) ;
136
+ } , [ loaderUrl ] ) ;
108
137
109
138
return status ;
110
139
} ;
0 commit comments