fix(examples): direct pro-example URLs no longer hang on Loading example
The load effect listed the overlay's `settled` flag as a dependency. On a direct /example/<pro-id> URL the load can begin the moment the overlay registers the example — one microtask before the overlay's import promise settles. The flip then re-fired the effect mid-load: the cleanup cancelled it (setReady skipped), the re-run hit the loadedIdRef guard and returned, and the page hung on "Loading example…" forever. The effect now depends only on the example itself; the 404-vs-still- loading decision moved into the render, where reading `settled` cancels nothing. A load cancelled mid-flight also resets the loadedIdRef guard so a genuine re-run reloads instead of early-returning.
This commit is contained in:
parent
08c5626b79
commit
159417c927
|
|
@ -67,24 +67,21 @@ export const ExampleEditorPage: React.FC = () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!exampleId) {
|
// `settled` is deliberately NOT a dependency. A direct link to a pro
|
||||||
setError(true);
|
// example can begin loading the moment the overlay registers it — one
|
||||||
return;
|
// microtask BEFORE the overlay's import promise settles. With `settled`
|
||||||
}
|
// in the deps, that flip re-fired the effect mid-load: the cleanup set
|
||||||
if (!example) {
|
// `cancelled`, setReady was skipped, and the re-run hit the loadedIdRef
|
||||||
// "Not in the gallery" and "not in the gallery YET" are different
|
// guard and returned — the page hung on "Loading example…" forever
|
||||||
// answers while the pro overlay's dynamic import is still in flight:
|
// (found with the reSpeaker example; any /example/<pro-id> direct URL
|
||||||
// a direct link to a pro example used to flash a 404 (marketing
|
// could lose this race). The 404 decision lives in the render below,
|
||||||
// header and all) before the overlay registered it and the editor
|
// where reading `settled` doesn't cancel anything.
|
||||||
// took over. Stay on the loading screen until the registry settles;
|
if (!exampleId || !example) return;
|
||||||
// only then is a missing id really a 404.
|
|
||||||
if (settled) setError(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (loadedIdRef.current === exampleId) return;
|
if (loadedIdRef.current === exampleId) return;
|
||||||
loadedIdRef.current = exampleId;
|
loadedIdRef.current = exampleId;
|
||||||
|
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
|
let done = false;
|
||||||
setReady(false);
|
setReady(false);
|
||||||
setError(false);
|
setError(false);
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|
@ -95,18 +92,29 @@ export const ExampleEditorPage: React.FC = () => {
|
||||||
// are swallowed inside ensureLibraries — anything that DOES bubble
|
// are swallowed inside ensureLibraries — anything that DOES bubble
|
||||||
// up here means the stores are partially populated. Surfacing a
|
// up here means the stores are partially populated. Surfacing a
|
||||||
// clean error is more useful than rendering an empty editor.
|
// clean error is more useful than rendering an empty editor.
|
||||||
|
done = true;
|
||||||
if (!cancelled) setError(true);
|
if (!cancelled) setError(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
done = true;
|
||||||
if (!cancelled) setReady(true);
|
if (!cancelled) setReady(true);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
|
// A load cancelled mid-flight must not poison the guard: if this
|
||||||
|
// effect re-runs for the same id, it has to actually reload instead
|
||||||
|
// of early-returning with `ready` still false.
|
||||||
|
if (!done && loadedIdRef.current === exampleId) {
|
||||||
|
loadedIdRef.current = null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [exampleId, example, settled]);
|
}, [exampleId, example]);
|
||||||
|
|
||||||
if (error) {
|
// "Not in the gallery" and "not in the gallery YET" are different answers
|
||||||
|
// while the pro overlay's dynamic import is still in flight: only once the
|
||||||
|
// registry settles is a missing id really a 404.
|
||||||
|
if (error || !exampleId || (settled && !example)) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue