Skip to content

Commit bf37c06

Browse files
committed
More elaborate
1 parent 010899c commit bf37c06

File tree

1 file changed

+80
-51
lines changed

1 file changed

+80
-51
lines changed

module/source/hooks/use-unity-loader.ts

Lines changed: 80 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ import { isBrowserEnvironment } from "../constants/is-browser-environment";
33
import { UnityLoaderStatus } from "../enums/unity-loader-status";
44
import { UnityConfig } from "../exports";
55

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>();
1115

1216
/**
1317
* Hook to embed a Unity Loader script.
@@ -25,86 +29,111 @@ const useUnityLoader = (unityConfig: UnityConfig): UnityLoaderStatus => {
2529
// It is possible for the application being rendered server side. In
2630
// this scenario, the window is not available. We can't create a Unity
2731
// Loader in this case.
28-
if (isBrowserEnvironment === false) {
32+
if (!isBrowserEnvironment) {
2933
return undefined;
3034
}
35+
3136
// If the script's source is null, we'll reset the status to idle.
32-
if (unityConfig.loaderUrl === null) {
37+
if (!loaderUrl) {
3338
setStatus(UnityLoaderStatus.Idle);
3439
return undefined;
3540
}
3641

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.
4144
let script = document.querySelector(
4245
`script[src="${loaderUrl}"]`
4346
) as HTMLScriptElement | null;
4447

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+
4651
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+
}
5666
} else {
67+
// Create a new script element.
5768
script = document.createElement("script");
5869
script.type = "text/javascript";
5970
script.src = loaderUrl;
6071
script.async = true;
6172
script.setAttribute("data-status", "loading");
6273
document.body.appendChild(script);
6374

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 = () => {
7177
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+
}
7683
setStatus(UnityLoaderStatus.Loaded);
77-
});
84+
};
7885

79-
script.addEventListener("error", () => {
86+
// Define error handler.
87+
const handleError = () => {
8088
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+
}
8594
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,
86107
});
108+
109+
setStatus(UnityLoaderStatus.Loading);
87110
}
88111

89-
// Remove event listeners on cleanup.
90112
return () => {
91113
const refData = scriptReferenceMap.get(loaderUrl);
114+
if (!refData) return;
115+
116+
// Decrement the ref count.
117+
refData.count -= 1;
92118

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);
104127
}
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);
105134
}
106135
};
107-
}, [unityConfig.loaderUrl]);
136+
}, [loaderUrl]);
108137

109138
return status;
110139
};

0 commit comments

Comments
 (0)