loadFile() reads fileUrl synchronously before its first await. When called from a \$effect, Svelte tracks that read and re-runs the effect every time fileUrl changes — i.e. after every successful load — causing an infinite cycle of file fetches and PdfViewer remounts. Fix: wrap the fileUrl read in untrack() so callers never accidentally subscribe to fileUrl changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
48 lines
1.1 KiB
TypeScript
48 lines
1.1 KiB
TypeScript
import { untrack } from 'svelte';
|
|
|
|
export function createFileLoader() {
|
|
let fileUrl = $state('');
|
|
let isLoading = $state(false);
|
|
let fileError = $state('');
|
|
|
|
async function loadFile(url: string): Promise<void> {
|
|
isLoading = true;
|
|
fileError = '';
|
|
// untrack prevents callers ($effect) from accidentally subscribing to fileUrl.
|
|
// Without it, the calling effect would re-run every time fileUrl changes (i.e.
|
|
// on every successful load), creating an infinite load loop.
|
|
const prev = untrack(() => fileUrl);
|
|
if (prev) URL.revokeObjectURL(prev);
|
|
fileUrl = '';
|
|
|
|
try {
|
|
const response = await fetch(url);
|
|
if (!response.ok) throw new Error('Failed to load file');
|
|
const blob = await response.blob();
|
|
fileUrl = URL.createObjectURL(blob);
|
|
} catch {
|
|
fileError = 'Vorschau konnte nicht geladen werden.';
|
|
} finally {
|
|
isLoading = false;
|
|
}
|
|
}
|
|
|
|
function destroy(): void {
|
|
if (fileUrl) URL.revokeObjectURL(fileUrl);
|
|
}
|
|
|
|
return {
|
|
get fileUrl() {
|
|
return fileUrl;
|
|
},
|
|
get isLoading() {
|
|
return isLoading;
|
|
},
|
|
get fileError() {
|
|
return fileError;
|
|
},
|
|
loadFile,
|
|
destroy
|
|
};
|
|
}
|