|
I have a pin button component: <script lang="ts">
import type { PinObject } from "$lib/schema";
import { isObjectPinned, toggleObjectPin } from "./pins.remote";
import Tooltip from "../Tooltip.svelte";
interface Props {
pinObject: PinObject;
}
let { pinObject }: Props = $props();
</script>
<button
class="icon-button group/tooltip relative size-10"
onclick={async () => await toggleObjectPin({ pinObject })}
>
{#await isObjectPinned(pinObject)}
<iconify-icon icon="fluent:arrow-clockwise-24-filled" class="animate-spin text-2xl"
></iconify-icon>
{:then pinState}
<Tooltip direction="bottom">
{#if pinState}
Unpin object
{:else}
Pin object
{/if}
</Tooltip>
<iconify-icon
icon={pinState ? "fluent:pin-off-24-filled" : "fluent:pin-24-filled"}
class="text-2xl"
></iconify-icon>
{/await}
</button>with the following remote functions: export const toggleObjectPin = command(SetObjectPin, async ({ pinObject, action }) => {
togglePin(pinObject, action)
void isObjectPinned(pinObject).refresh();
void getPinnedObjects().refresh();
})
export const getPinnedObjects = query(async () => {
return getPinCookie();
});
export const isObjectPinned = query(PinObject, async (pinObject) => {
const pinned = getPinCookie();
return pinned.some(o => deepEqual(o, pinObject))
})Whenever I directly inline the pin button in a page, it correctly updates. However, when used in a component, it does not update when clicking the button, and a derived inert warning is thrown. I have no idea why. What am I doing wrong? |
Replies: 2 comments
|
I think the issue is that the command is refreshing a query by constructing a fresh query call on the server: void isObjectPinned(pinObject).refresh()That is not always the same as refreshing the client-requested query instance that your component is currently awaiting. For remote functions, the safer pattern inside a Something like: import { requested } from '$app/server'
export const toggleObjectPin = command(SetObjectPin, async ({ pinObject, action }) => {
await togglePin(pinObject, action)
await requested(isObjectPinned, 10).refreshAll()
await getPinnedObjects().refresh()
})Or, if you only want to refresh matching pins, loop the requested args: for (const { arg, query } of requested(isObjectPinned, 10)) {
if (deepEqual(arg, pinObject)) {
void query.refresh()
}
}SvelteKit will wait for those server-side refreshes and send the refreshed query data back with the command response. Two other things I would double-check:
So my read is: the component itself is probably not the fundamental problem; the stale behavior is more likely from refreshing the wrong remote query cache entry, plus possibly racing the mutation. |
|
I finally got it working after some painful debugging. Seemed like I was doing multiple things wrong.
In the end, this is what worked for me: <button
class="icon-button group/tooltip relative size-10"
onclick={async () => await toggleObjectPin({ pinObject })}
>
<svelte:boundary>
{#snippet pending()}
<iconify-icon icon="fluent:arrow-clockwise-24-filled" class="animate-spin text-2xl"
></iconify-icon>
{/snippet}
{const pinState = $derived(isObjectPinned(pinObject))}
{#if await pinState}
<Tooltip direction="bottom">Object losmaken</Tooltip>
<iconify-icon icon="fluent:pin-off-24-filled" class="text-2xl"></iconify-icon>
{:else}
<Tooltip direction="bottom">Object vastzetten</Tooltip>
<iconify-icon icon="fluent:pin-24-filled" class="text-2xl"></iconify-icon>
{/if}
</svelte:boundary>
</button>If I am wrong about anything stated here, please let me know. |
I finally got it working after some painful debugging. Seemed like I was doing multiple things wrong.
{#await}blocks with remote functions (from #15655). Instead, svelte:boundary should be used (the new async-await syntax).await pinState ? "" : ""ternary statement with a web component like iconify-icon. This will throw a very confusing error that got me stuck for a while.In the end, this is what worked for me: