From 3ea7b2a514e1e689185ab3c9de379a7276e1e311 Mon Sep 17 00:00:00 2001
From: Lachlan Collins <1667261+lachlancollins@users.noreply.github.com>
Date: Thu, 6 Jul 2023 17:47:56 +0200
Subject: [PATCH 1/4] Update reactivity docs
Use writable/derived stores
---
docs/svelte/reactivity.md | 34 ++++++++++++++++++----------------
1 file changed, 18 insertions(+), 16 deletions(-)
diff --git a/docs/svelte/reactivity.md b/docs/svelte/reactivity.md
index c4c8a105f9..675e063b42 100644
--- a/docs/svelte/reactivity.md
+++ b/docs/svelte/reactivity.md
@@ -3,18 +3,18 @@ id: reactivity
title: Reactivity
---
-Svelte uses a compiler to build your code which optimises rendering. By default, variables will run once, unless they are referenced in your markup. To be able to react to changes in options you need to use [stores](https://svelte.dev/docs/svelte-store).
+Svelte uses a compiler to build your code which optimises rendering. By default, components run once, unless they are referenced in your markup. To be able to react to changes in options you need to use [stores](https://svelte.dev/docs/svelte-store).
-In the below example, the `refetchInterval` option is set from the variable `intervalMs`, which is edited by the input field. However, as the query is not told it should react to changes in `intervalMs`, `refetchInterval` will not change when the input value changes.
+In the below example, the `refetchInterval` option is set from the variable `intervalMs`, which is bound to the input field. However, as the query is not able to react to changes in `intervalMs`, `refetchInterval` will not change when the input value changes.
```markdown
-
-
+
```
-To solve this, create a store for the options and use it as input for the query. Update the options store when the value changes and the query will react to the change.
+To solve this, we can convert `intervalMs` into a writable store. The query options can then be turned into a derived store, which will be passed into the function with true reactivity.
```markdown
-
-
+
```
From a8e1dbde83f74ca44c2d5ea62a27c3f9832c39cc Mon Sep 17 00:00:00 2001
From: Lachlan Collins <1667261+lachlancollins@users.noreply.github.com>
Date: Thu, 6 Jul 2023 18:08:09 +0200
Subject: [PATCH 2/4] Add test for reactivity bug
---
.../src/__tests__/CreateQuery.svelte | 11 ++--
.../src/__tests__/createQuery.test.ts | 63 ++++++++++++++++---
2 files changed, 60 insertions(+), 14 deletions(-)
diff --git a/packages/svelte-query/src/__tests__/CreateQuery.svelte b/packages/svelte-query/src/__tests__/CreateQuery.svelte
index c702c66814..383d2e74f4 100644
--- a/packages/svelte-query/src/__tests__/CreateQuery.svelte
+++ b/packages/svelte-query/src/__tests__/CreateQuery.svelte
@@ -1,15 +1,12 @@
{#if $query.isPending}
@@ -17,7 +14,7 @@
{:else if $query.isError}
Error
{:else if $query.isSuccess}
- Success
+ {$query.data}
{/if}
diff --git a/packages/svelte-query/src/__tests__/createQuery.test.ts b/packages/svelte-query/src/__tests__/createQuery.test.ts
index 47be18395d..f872f22e0f 100644
--- a/packages/svelte-query/src/__tests__/createQuery.test.ts
+++ b/packages/svelte-query/src/__tests__/createQuery.test.ts
@@ -1,6 +1,7 @@
import { describe, expect, test } from 'vitest'
import { render, waitFor } from '@testing-library/svelte'
import { derived, writable } from 'svelte/store'
+import { QueryClient } from '@tanstack/query-core'
import CreateQuery from './CreateQuery.svelte'
import { sleep } from './utils'
import type { CreateQueryOptions } from '../types'
@@ -16,20 +17,21 @@ describe('createQuery', () => {
return 'Success'
},
},
+ queryClient: new QueryClient(),
},
})
await waitFor(() => {
- expect(rendered.getByText('Loading')).toBeInTheDocument()
+ expect(rendered.queryByText('Loading')).toBeInTheDocument()
})
await waitFor(() => {
- expect(rendered.getByText('Success')).toBeInTheDocument()
+ expect(rendered.queryByText('Success')).toBeInTheDocument()
})
})
test('Keep previous data when returned as placeholder data', async () => {
- const options = writable({
+ const optionsStore = writable({
queryKey: ['test', [1]],
queryFn: async ({ queryKey }) => {
await sleep(10)
@@ -40,7 +42,12 @@ describe('createQuery', () => {
placeholderData: (previousData: { id: number }[]) => previousData,
}) satisfies CreateQueryOptions
- const rendered = render(CreateQuery, { props: { options } })
+ const rendered = render(CreateQuery, {
+ props: {
+ options: optionsStore,
+ queryClient: new QueryClient(),
+ },
+ })
await waitFor(() => {
expect(rendered.queryByText('id: 1')).not.toBeInTheDocument()
@@ -52,7 +59,7 @@ describe('createQuery', () => {
expect(rendered.queryByText('id: 2')).not.toBeInTheDocument()
})
- options.update((o) => ({ ...o, queryKey: ['test', [1, 2]] }))
+ optionsStore.update((o) => ({ ...o, queryKey: ['test', [1, 2]] }))
await waitFor(() => {
expect(rendered.queryByText('id: 1')).toBeInTheDocument()
@@ -77,11 +84,12 @@ describe('createQuery', () => {
const rendered = render(CreateQuery, {
props: {
options: optionsStore,
+ queryClient: new QueryClient(),
},
})
await waitFor(() => {
- expect(rendered.getByText('Success')).toBeInTheDocument()
+ expect(rendered.queryByText('Success')).toBeInTheDocument()
})
})
@@ -99,11 +107,52 @@ describe('createQuery', () => {
const rendered = render(CreateQuery, {
props: {
options: derivedStore,
+ queryClient: new QueryClient(),
+ },
+ })
+
+ await waitFor(() => {
+ expect(rendered.queryByText('Success')).toBeInTheDocument()
+ })
+ })
+
+ test('Ensure reactivity when queryClient defaults are set', async () => {
+ const writableStore = writable(1)
+
+ const derivedStore = derived(writableStore, ($store) => ({
+ queryKey: [$store],
+ queryFn: async () => {
+ await sleep(10)
+ return `Success ${$store}`
+ },
+ })) satisfies CreateQueryOptions
+
+ const rendered = render(CreateQuery, {
+ props: {
+ options: derivedStore,
+ queryClient: new QueryClient({
+ defaultOptions: { queries: { staleTime: 60 * 1000 } },
+ }),
},
})
await waitFor(() => {
- expect(rendered.getByText('Success')).toBeInTheDocument()
+ expect(rendered.queryByText('Success 1')).toBeInTheDocument()
+ expect(rendered.queryByText('Success 2')).not.toBeInTheDocument()
+ })
+
+ writableStore.set(2)
+
+ await waitFor(() => {
+ expect(rendered.queryByText('Success 1')).not.toBeInTheDocument()
+ expect(rendered.queryByText('Success 2')).toBeInTheDocument()
+ })
+
+ writableStore.set(1)
+
+ await waitFor(() => {
+ expect(rendered.queryByText('Success 1')).toBeInTheDocument()
+ expect(rendered.queryByText('Success 2')).not.toBeInTheDocument()
})
})
})
From 637c70b96b1499ef3a8dcec2625ca4701c67751a Mon Sep 17 00:00:00 2001
From: Lachlan Collins <1667261+lachlancollins@users.noreply.github.com>
Date: Thu, 6 Jul 2023 18:20:58 +0200
Subject: [PATCH 3/4] Refactor placeholderData test
---
.../src/__tests__/CreateQuery.svelte | 14 +--
.../src/__tests__/createQuery.test.ts | 91 +++++++++----------
2 files changed, 52 insertions(+), 53 deletions(-)
diff --git a/packages/svelte-query/src/__tests__/CreateQuery.svelte b/packages/svelte-query/src/__tests__/CreateQuery.svelte
index 383d2e74f4..05029b0b81 100644
--- a/packages/svelte-query/src/__tests__/CreateQuery.svelte
+++ b/packages/svelte-query/src/__tests__/CreateQuery.svelte
@@ -14,11 +14,11 @@
{:else if $query.isError}
Error
{:else if $query.isSuccess}
- {$query.data}
+ {#if Array.isArray($query.data)}
+ {#each $query.data as item}
+ {item}
+ {/each}
+ {:else}
+ {$query.data}
+ {/if}
{/if}
-
-
- {#each $query.data ?? [] as entry}
- id: {entry.id}
- {/each}
-
diff --git a/packages/svelte-query/src/__tests__/createQuery.test.ts b/packages/svelte-query/src/__tests__/createQuery.test.ts
index f872f22e0f..6f7f08d7c8 100644
--- a/packages/svelte-query/src/__tests__/createQuery.test.ts
+++ b/packages/svelte-query/src/__tests__/createQuery.test.ts
@@ -4,7 +4,6 @@ import { derived, writable } from 'svelte/store'
import { QueryClient } from '@tanstack/query-core'
import CreateQuery from './CreateQuery.svelte'
import { sleep } from './utils'
-import type { CreateQueryOptions } from '../types'
describe('createQuery', () => {
test('Render and wait for success', async () => {
@@ -30,48 +29,6 @@ describe('createQuery', () => {
})
})
- test('Keep previous data when returned as placeholder data', async () => {
- const optionsStore = writable({
- queryKey: ['test', [1]],
- queryFn: async ({ queryKey }) => {
- await sleep(10)
- const ids = queryKey[1]
- if (!ids || !Array.isArray(ids)) return []
- return ids.map((id) => ({ id }))
- },
- placeholderData: (previousData: { id: number }[]) => previousData,
- }) satisfies CreateQueryOptions
-
- const rendered = render(CreateQuery, {
- props: {
- options: optionsStore,
- queryClient: new QueryClient(),
- },
- })
-
- await waitFor(() => {
- expect(rendered.queryByText('id: 1')).not.toBeInTheDocument()
- expect(rendered.queryByText('id: 2')).not.toBeInTheDocument()
- })
-
- await waitFor(() => {
- expect(rendered.queryByText('id: 1')).toBeInTheDocument()
- expect(rendered.queryByText('id: 2')).not.toBeInTheDocument()
- })
-
- optionsStore.update((o) => ({ ...o, queryKey: ['test', [1, 2]] }))
-
- await waitFor(() => {
- expect(rendered.queryByText('id: 1')).toBeInTheDocument()
- expect(rendered.queryByText('id: 2')).not.toBeInTheDocument()
- })
-
- await waitFor(() => {
- expect(rendered.queryByText('id: 1')).toBeInTheDocument()
- expect(rendered.queryByText('id: 2')).toBeInTheDocument()
- })
- })
-
test('Accept a writable store for options', async () => {
const optionsStore = writable({
queryKey: ['test'],
@@ -79,7 +36,7 @@ describe('createQuery', () => {
await sleep(10)
return 'Success'
},
- }) satisfies CreateQueryOptions
+ })
const rendered = render(CreateQuery, {
props: {
@@ -102,7 +59,7 @@ describe('createQuery', () => {
await sleep(10)
return 'Success'
},
- })) satisfies CreateQueryOptions
+ }))
const rendered = render(CreateQuery, {
props: {
@@ -125,7 +82,7 @@ describe('createQuery', () => {
await sleep(10)
return `Success ${$store}`
},
- })) satisfies CreateQueryOptions
+ }))
const rendered = render(CreateQuery, {
props: {
@@ -155,4 +112,46 @@ describe('createQuery', () => {
expect(rendered.queryByText('Success 2')).not.toBeInTheDocument()
})
})
+
+ test('Keep previous data when returned as placeholder data', async () => {
+ const writableStore = writable([1])
+
+ const derivedStore = derived(writableStore, ($store) => ({
+ queryKey: ['test', $store],
+ queryFn: async () => {
+ await sleep(10)
+ return $store.map((id) => `Success ${id}`)
+ },
+ placeholderData: (previousData: string) => previousData,
+ }))
+
+ const rendered = render(CreateQuery, {
+ props: {
+ options: derivedStore,
+ queryClient: new QueryClient(),
+ },
+ })
+
+ await waitFor(() => {
+ expect(rendered.queryByText('Success 1')).not.toBeInTheDocument()
+ expect(rendered.queryByText('Success 2')).not.toBeInTheDocument()
+ })
+
+ await waitFor(() => {
+ expect(rendered.queryByText('Success 1')).toBeInTheDocument()
+ expect(rendered.queryByText('Success 2')).not.toBeInTheDocument()
+ })
+
+ writableStore.set([1, 2])
+
+ await waitFor(() => {
+ expect(rendered.queryByText('Success 1')).toBeInTheDocument()
+ expect(rendered.queryByText('Success 2')).not.toBeInTheDocument()
+ })
+
+ await waitFor(() => {
+ expect(rendered.queryByText('Success 1')).toBeInTheDocument()
+ expect(rendered.queryByText('Success 2')).toBeInTheDocument()
+ })
+ })
})
From 4fdcfebe525c8625f8a9604b3c0204a2864a04e2 Mon Sep 17 00:00:00 2001
From: Lachlan Collins <1667261+lachlancollins@users.noreply.github.com>
Date: Thu, 6 Jul 2023 18:32:15 +0200
Subject: [PATCH 4/4] Fix prettier
---
packages/svelte-query/src/__tests__/createQuery.test.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/svelte-query/src/__tests__/createQuery.test.ts b/packages/svelte-query/src/__tests__/createQuery.test.ts
index 6f7f08d7c8..373494e179 100644
--- a/packages/svelte-query/src/__tests__/createQuery.test.ts
+++ b/packages/svelte-query/src/__tests__/createQuery.test.ts
@@ -112,7 +112,7 @@ describe('createQuery', () => {
expect(rendered.queryByText('Success 2')).not.toBeInTheDocument()
})
})
-
+
test('Keep previous data when returned as placeholder data', async () => {
const writableStore = writable([1])