Skip to content

Svelte 5: asynchronous derived api #13722

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
HighFunctioningSociopathSH opened this issue Oct 20, 2024 · 6 comments
Closed

Svelte 5: asynchronous derived api #13722

HighFunctioningSociopathSH opened this issue Oct 20, 2024 · 6 comments

Comments

@HighFunctioningSociopathSH
Copy link

HighFunctioningSociopathSH commented Oct 20, 2024

Describe the problem

I love the api surrounding the derived store which also allows you to asynchronously update the value of the store and I was wondering whether we can have something like that when working with the new $derived rune. This could prevent usage of $effect rune in many scenarios.

Describe the proposed solution

In the current api of the derived stores, derived takes the dependecy as its first argument, the callback to update the derivedStore as its second and the intialValue as its third argument. The sad part is that it only accepts stores as its first argument. Maybe we can have something similar but with $state or other $derived runes as the dependency. like below

<script lang="ts">
  let condition = $state(true);
  const delayedCondition = $derived.async(condition, (newCondition, set) => {
    const timeout = setTimeout(() => {
      set(newCondition)
    }, 2000)
    return () => {
      clearTimeout(timeout);
    }
  }, condition)
</script>

Where the first argument is the dependency $state or $derived, the second is the callback function and the third is the initial value of the delayedCondition.

Importance

nice to have

@brunnerh
Copy link
Member

Like an effect that explicitly tracks dependencies, this looks like something that can be built in userland via $state & $effect. It might be a bit a bit more verbose but that probably does not justify adding a new rune.

@webJose
Copy link
Contributor

webJose commented Oct 20, 2024

Personally, I like setting promises in $derived variables. This is a sample component I wrote less than 1 hour ago for blog article:

<script lang="ts">
    import type { MyData } from './my-types.js';

    type Props = {
        pageSize?: number;
    };

    let {
        pageSize = 20,
    }: Props = $props();

    let data = $derived(fetchMyData());

    async function fetchMyData() {
        const response = await fetch(`/api/my-data?pageSize=${pageSize}`, { ... });
        if (response.ok) {
            return JSON.parse(await response.json()) as MyData[];
        }
        return null;
    }
</script>

{#await data}
    <Spinner>Loading data...</Spinner>
{:then items}
    <ul>
        {#each items as item (item.id)}
            <li>
                <!-- Render data here -->
            </li>
        {/each}
    <ul>
{:catch reason}
    <span>Oops! - {reason}</span>
{/await}

The fact that the $derived variable can hold the promise is in perfect harmony with {#await} blocks. Yes, I suppose that there are use cases for everything, and maybe yours is justified. I just thought of adding this comment just in case other people are coming here not realizing that this works great with {#await}.

@HighFunctioningSociopathSH
Copy link
Author

@webJose That is actually a great approach. Thanks for the insight

@sysmat
Copy link

sysmat commented Feb 9, 2025

This is not working in 5.19.9. Data is now object with $on, $state

@diramazioni
Copy link

It seems partially working from the functionality perspective, but placing a fetching derived during the initialization cause on SSR this logs:

Cannot call `fetch` eagerly during server side rendering with relative URL ([url]) — put your `fetch` calls inside `onMount` or a `load` function instead

and since is inside a component that would require either to load the data from the serverside to the parent and pass them to che child, or use an $effect/onMount where I can't declare a derived

@webJose
Copy link
Contributor

webJose commented Apr 14, 2025

@diramazioni if you're talking about my proposed approach, then yes, Sveltekit is special because when running on the server side, Node's fetch function needs a URL with host, scheme and port. So in this case, you might want to defer the execution to the onMount() call, or use Sveltekit's built-in fetch functionality inside a load() function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants