Skip to content

Commit

Permalink
use svelte rune to update props in frontend reports
Browse files Browse the repository at this point in the history
For now, this makes the props passed to these components deeply reactive
as there is no easy way to opt out of it right now. This might slightly
impact performance as all the props are proxified.
  • Loading branch information
yagebu committed Dec 28, 2024
1 parent e74d974 commit 958c795
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 14 deletions.
13 changes: 13 additions & 0 deletions frontend/src/reports/route.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/** Create a reactive props proxy to allow updating of props. */
export function updateable_props<T extends Record<string, unknown>>(
raw_props: T,
): [props: T, update: (v: T) => void] {
// TODO: this makes it deeply reactive, which adds unnecessary overhead
const props = $state(raw_props);
return [
props,
(new_props) => {
Object.assign(props, new_props);
},
];
}
39 changes: 25 additions & 14 deletions frontend/src/reports/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { type Component, mount, unmount } from "svelte";

import { log_error } from "../log";
import ErrorSvelte from "./Error.svelte";
import { updateable_props } from "./route.svelte";

export interface FrontendRoute {
readonly report: string;
Expand All @@ -15,10 +16,17 @@ export interface FrontendRoute {
}

/** This class pairs the components and their load functions to use them in a type-safe way. */
export class Route<T extends Record<string, unknown>> implements FrontendRoute {
// The base type for the component props needs to be typed as Record<string,any> to allow for T
// to be correctly inferred from the imported svelte components
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export class Route<T extends Record<string, any>> implements FrontendRoute {
/** The currently rendered instance - if loading failed, we render an error component. */
private instance?:
| { error: false; component: Record<string, unknown> }
| {
error: false;
component: Record<string, unknown>;
update_props: (v: T) => void;
}
| { error: true; component: Record<string, unknown> };

/** The currently rendered URL. */
Expand Down Expand Up @@ -46,7 +54,7 @@ export class Route<T extends Record<string, unknown>> implements FrontendRoute {
/** Destroy any components that might be rendered by this route. */
destroy(): void {
if (this.instance !== undefined) {
unmount(this.instance.component);
void unmount(this.instance.component);
}
this.instance = undefined;
}
Expand All @@ -61,17 +69,20 @@ export class Route<T extends Record<string, unknown>> implements FrontendRoute {
previous?.destroy();
}
try {
const props = await this.load(url);
// Check if the component is changed - otherwise only update the data.
// Svelte 5 removed component.$set - so always re-render now
// if (previous === this && this.instance?.error === false) {
//this.instance.component.$set(props);
this.destroy();
target.innerHTML = "";
this.instance = {
error: false,
component: mount(this.Component, { target, props }),
};
const raw_props = await this.load(url);
// Check if the component is unchanged and only update the data in this case.
if (previous === this && this.instance?.error === false) {
this.instance.update_props(raw_props);
} else {
this.destroy();
target.innerHTML = "";
const [props, update_props] = updateable_props(raw_props);
this.instance = {
error: false,
component: mount(this.Component, { target, props }),
update_props,
};
}
} catch (error: unknown) {
log_error(error);
if (error instanceof Error) {
Expand Down

0 comments on commit 958c795

Please sign in to comment.