Here's the order of what's happening:
+page.server.ts
: The initial load works as expected+page.svelte
: data:PageData
is populated correctly+page.server.ts
: Action
executes and works as expected+page.server.ts
: loads again with the new data+page.svelte
: doesn't reload at allHere's what I expected:
+page.svelte
to reload because page.server.ts
returned new dataAnalysis:
At first, I thought maybe my hooks.server.ts
was causing issues, so I removed the functions and the issue still persisted.
There doesn't seem to be a need to invalidate
anything because the form update
is working and page.server.ts
is reloading as expected.
+page.server.ts
:
// src/routes/admin/manage-roles/+page.server.ts
// (...) import statements all working: db, schemas, types, etc...
export const load: PageServerLoad = async ({ locals }) => {
try {
const allRoles = await db.select().from(roles)
return {
allRoles
};
} catch (error) {
console.error("Error fetching roles:", error);
return fail(500, { error: error.message });
}
};
export const actions: Actions = {
new: async ({ request }) => {
const form = await superValidate(request, roleSchema);
if (!form.valid) {
return fail(400, {
form
});
}
try {
const id = uuidv4();
const result = await db.insert(roles).values({
id,
name: form?.data?.name
});
} catch (error) {
console.error("Error creating role:", error);
return fail(500, { error: error.message });
}
return { success: true };
}
};
+page.svelte
:
<script lang="ts">
// (...) import statements all working: db, schemas, components, etc...
export let data: PageData;
const { allRoles } = data;
const roleStore = writable(allRoles);
console.log("allRoles?", allRoles);
const isNewDialogOpen = writable(false);
const isSubmittingForm = writable(false);
const table = createTable(roleStore, {
sort: addSortBy({ disableMultiSort: true }),
page: addPagination(),
filter: addTableFilter({
fn: ({ filterValue, value }) => value.includes(filterValue)
}),
select: addSelectedRows(),
hide: addHiddenColumns()
});
// (...) custom table control events, etc... all working.
</script>
<!-- New Role Dialog -->
<Dialog
isOpen={$isNewDialogOpen}
onOpenChange={(open) => {
$isNewDialogOpen = open;
}}
title="New Role"
description="Add a new role to assign to users.">
<div slot="body">
<Form
action="?/new"
onBeforeSubmit={() => ($isSubmittingForm = true)}
on:successAfterUpdate={async () => {
$isSubmittingForm = false;
$isNewDialogOpen = false;
}}>
<div class="grid gap-2">
<div class="grid gap-1">
<Label class="sr-only" for="name">Role Name</Label>
<Input id="name" name="name" placeholder="Admin" required />
</div>
<div class="flex w-full justify-end">
<Button type="submit" class="w-32" disabled={$isSubmittingForm}>Create Role</Button>
</div>
</div>
</Form>
</div>
<div slot="footer" />
</Dialog>
<!-- Roles -->
<div class="flex flex-col px-10">
<div class="flex-1 mb-6">
<div class="flex items-center justify-between mb-2">
<h2 class="text-2xl font-bold tracking-tight mb-4">
<Users class="header-icon" />Manage Roles
</h2>
<div class="flex items-center space-x-2">
<Button size="sm" on:click={() => ($isNewDialogOpen = true)}>
<Plus class="mr-2 h-4 w-4" />
New
</Button>
</div>
</div>
<ManageNav active="roles" />
</div>
<!-- (...) The rest of the table/UI -->
hooks.server.ts
:
import { auth } from "$lib/server/lucia";
import type { Handle, HandleServerError } from "@sveltejs/kit";
export const handleError: HandleServerError = async ({ error, event }) => {
const errorId = crypto.randomUUID();
event.locals.error = error?.toString() || undefined;
event.locals.errorStackTrace = error?.stack || undefined;
event.locals.errorId = errorId;
console.error(`Error ID: ${errorId}`, error)
return {
message: "An unexpected error occurred.",
errorId
};
};
export const handle: Handle = async ({ event, resolve }) => {
event.locals.auth = auth.handleRequest(event)
return await resolve(event)
};
The page does not reload, it should only fetch new data and update the data
property. That means everything that depends on data
has to be reactive to update correctly, so e.g.:
$: ({ allRoles } = data); // reactive destructuring requires parentheses
For things like stores it depends on how they are used, if passed to a context, you probably would e.g. want to instantiate it once and only update the instance reactively. Note that the page
store by default also provides access to the page data anywhere in the route so the roleStore
might be redundant anyway.
The other stores (isNewDialogOpen
/isSubmittingForm
) also seem pointless, you can just use regular variables if the store never leaves the component. Local state is reactive by default (changes cause the DOM to update).