vishalbalaji / trpc-svelte-query-adapter

A simple adapter to use `@tanstack/svelte-query` with trpc, similar to `@trpc/react-query`.
71 stars 6 forks source link

How to run queries outside of component initialization? #40

Closed sheldonbaker closed 3 months ago

sheldonbaker commented 3 months ago

In trpc-sveltekit, the following is possible (from https://icflorescu.github.io/trpc-sveltekit/getting-started):

<script lang="ts">
  import { page } from '$app/stores';
  import { trpc } from '$lib/trpc/client';

  let greeting = 'press the button to load data';
  let loading = false;

  const loadData = async () => {
    loading = true;
    greeting = await trpc($page).greeting.query(); // (substituted with `createQuery()`)
    loading = false;
  };
</script>

<h6>Loading data in<br /><code>+page.svelte</code></h6>

<a
  href="#load"
  role="button"
  class="secondary"
  aria-busy={loading}
  on:click|preventDefault={loadData}>Load</a
>
<p>{greeting}</p>

Attempting to implement this pattern with trpc-svelte-query-adapter results in an error of Function called outside component initialization. It seems due to the fact that svelte-query's createQuery method is called (by trpc-svelte-query-adapter) without it being passed a queryClient, so it tries to get it from the current component context, which of course doesn't exist outside of component initialization.

Is there a different way to implement this pattern, or would modifying these lines to pass in a queryClient be better?

vishalbalaji commented 3 months ago

Hi @sheldonbaker!

What you are asking here is a fairly common question topic of discussion in the Tanstack Query community. By default, Tanstack Query's behaviour is to run the queries at component initialization, which means that they have to be executed eagerly and therefore have to be created at the top level of a component. This is true for all frameworks, such as React, Solid, etc.

Here are some past discussions for reference. These are relating to React, but the same principle applies here as well: https://github.com/TanStack/query/discussions/1205 https://www.reddit.com/r/reactjs/comments/18fk8py/reactquery_is_there_a_proper_way_to_enable_query/

In order to load these queries lazily(on a button press, for example), you would have to use the enabled property of the query. You could do this one of two ways:

  1. Disable the query and use $query.refetch to fetch the data manually:

    
    <script lang="ts">
    import { page } from "$app/stores";
    import { trpc } from "$lib/trpc/client";
    
    const api = trpc($page);
    const query = api.greeting.createQuery("foo", {
    enabled: false,
    staleTime: Infinity,
    });
    
    let greeting = "press the button to load data";
    
    const loadData = async () => {
    greeting = (await $query.refetch()).data!; // (substituted with `createQuery()`)
    };
    </script>
Loading data in
+page.svelte

<a href="#load" role="button" class="secondary" aria-busy={$query.isLoading} on:click|preventDefault={loadData}

Load

{greeting}


2. Use a variable to control the `enabled` state of the query:
```svelte
<script lang="ts">
import { page } from "$app/stores";
import { trpc } from "$lib/trpc/client";

const api = trpc($page); let enabled = false; $: query = api.greeting.createQuery("foo", { enabled, staleTime: Infinity, });

const loadData = async () => { enabled = true; };

Loading data in
+page.svelte

<a href="#load" role="button" class="secondary" aria-busy={$query.isLoading} on:click|preventDefault={loadData}

Load

{$query.data ?? "press the button to load data"}

I would recommend the second approach, since the first approach only fetches the data but does not cache it.

Since the goal of the library is to serve as a thin wrapper around the tRPC client, I don't think that I would be adding anything for this for the time being. Hope that this was helpful to you though.

vishalbalaji commented 3 months ago

Closing the issue.