diff --git a/.changeset/sharp-ravens-hear.md b/.changeset/sharp-ravens-hear.md
new file mode 100644
index 0000000000..e5b5868c2b
--- /dev/null
+++ b/.changeset/sharp-ravens-hear.md
@@ -0,0 +1,7 @@
+---
+'houdini': patch
+'houdini-react': patch
+'houdini-svelte': patch
+---
+
+isFetching will switch only when a network call is happening (and starts at true for queries)
diff --git a/e2e/sveltekit/src/lib/utils/routes.ts b/e2e/sveltekit/src/lib/utils/routes.ts
index 0cc1bc11cd..0c7e46f9e2 100644
--- a/e2e/sveltekit/src/lib/utils/routes.ts
+++ b/e2e/sveltekit/src/lib/utils/routes.ts
@@ -4,6 +4,8 @@ export const routes = {
// features
Query_param: '/query-param',
+ isFetching_with_load: '/isFetching/with_load',
+ isFetching_without_load: '/isFetching/without_load',
Stores_SSR: '/stores/ssr',
Stores_Network: '/stores/network',
diff --git a/e2e/sveltekit/src/routes/isFetching/spec.ts b/e2e/sveltekit/src/routes/isFetching/spec.ts
new file mode 100644
index 0000000000..e9e1628cfe
--- /dev/null
+++ b/e2e/sveltekit/src/routes/isFetching/spec.ts
@@ -0,0 +1,60 @@
+import { expect, test } from '@playwright/test';
+import { routes } from '../../lib/utils/routes.js';
+import { clientSideNavigation, goto, locator_click } from '../../lib/utils/testsHelper.js';
+
+test.describe('isFetching', () => {
+ test('with_load SSR', async ({ page }) => {
+ const [msg] = await Promise.all([
+ page.waitForEvent('console'),
+ goto(page, routes.isFetching_with_load)
+ ]);
+
+ expect(msg.text()).toBe('with_load - isFetching: false');
+ });
+
+ test('with_load CSR', async ({ page }) => {
+ await goto(page, routes.Home);
+
+ // Switch page and check directly the first console log
+ const [msg] = await Promise.all([
+ page.waitForEvent('console'),
+ clientSideNavigation(page, routes.isFetching_with_load)
+ ]);
+ expect(msg.text()).toBe('with_load - isFetching: true');
+
+ // wait for the isFetching false
+ const msg2 = await page.waitForEvent('console');
+ expect(msg2.text()).toBe('with_load - isFetching: false');
+ });
+
+ test('without_load CSR', async ({ page }) => {
+ await goto(page, routes.Home);
+
+ // Switch page and check the first console log
+ // It's expected to stay true until the first fetch!
+ const [msg] = await Promise.all([
+ page.waitForEvent('console'),
+ clientSideNavigation(page, routes.isFetching_without_load)
+ ]);
+ expect(msg.text()).toBe('without_load - isFetching: true');
+
+ const [msg2] = await Promise.all([
+ page.waitForEvent('console'),
+ // manual fetch
+ locator_click(page, 'button')
+ ]);
+ expect(msg2.text()).toBe('without_load - isFetching: true');
+
+ // wait for the isFetching false
+ const msg3 = await page.waitForEvent('console');
+ expect(msg3.text()).toBe('without_load - isFetching: false');
+
+ // second click should not refetch... so isFetching should be false
+ const [msg4] = await Promise.all([
+ page.waitForEvent('console'),
+ // manual fetch
+ locator_click(page, 'button')
+ ]);
+ expect(msg4.text()).toBe('without_load - isFetching: false');
+ });
+});
diff --git a/e2e/sveltekit/src/routes/isFetching/with_load/+page.svelte b/e2e/sveltekit/src/routes/isFetching/with_load/+page.svelte
new file mode 100644
index 0000000000..f05fb84aa9
--- /dev/null
+++ b/e2e/sveltekit/src/routes/isFetching/with_load/+page.svelte
@@ -0,0 +1,16 @@
+
+
+
{JSON.stringify($store, null, 2)}
diff --git a/e2e/sveltekit/src/routes/isFetching/without_load/+page.svelte b/e2e/sveltekit/src/routes/isFetching/without_load/+page.svelte
new file mode 100644
index 0000000000..c859d7024b
--- /dev/null
+++ b/e2e/sveltekit/src/routes/isFetching/without_load/+page.svelte
@@ -0,0 +1,22 @@
+
+
+
+
+{JSON.stringify($store, null, 2)}
diff --git a/packages/houdini-react/src/runtime/index.ts b/packages/houdini-react/src/runtime/index.ts
index 3b4ec839bc..01f99b28a4 100644
--- a/packages/houdini-react/src/runtime/index.ts
+++ b/packages/houdini-react/src/runtime/index.ts
@@ -14,6 +14,9 @@ export async function query(artifact: QueryArtifact, variables?: any) {
session: {},
metadata: {},
},
+ setFetching: () => {
+ console.log('fetching...')
+ },
})
return [result.result]
diff --git a/packages/houdini-svelte/src/runtime/stores/mutation.ts b/packages/houdini-svelte/src/runtime/stores/mutation.ts
index 87fb39549c..e8beda067b 100644
--- a/packages/houdini-svelte/src/runtime/stores/mutation.ts
+++ b/packages/houdini-svelte/src/runtime/stores/mutation.ts
@@ -20,10 +20,14 @@ export class MutationStore<
private store: Writable>
+ protected setFetching(isFetching: boolean) {
+ this.store?.update((s) => ({ ...s, isFetching }))
+ }
+
constructor({ artifact }: { artifact: MutationArtifact }) {
super()
this.artifact = artifact
- this.store = writable(this.nullState)
+ this.store = writable(this.initialState)
}
async mutate(
@@ -85,6 +89,7 @@ export class MutationStore<
artifact: this.artifact,
variables: newVariables,
session: await getSession(),
+ setFetching: (val) => this.setFetching(val),
cached: false,
metadata,
fetch,
@@ -161,7 +166,7 @@ export class MutationStore<
return this.store.subscribe(...args)
}
- private get nullState() {
+ private get initialState() {
return {
data: null as _Data | null,
errors: null,
diff --git a/packages/houdini-svelte/src/runtime/stores/pagination/cursor.ts b/packages/houdini-svelte/src/runtime/stores/pagination/cursor.ts
index 515f2a7363..2fa22c9398 100644
--- a/packages/houdini-svelte/src/runtime/stores/pagination/cursor.ts
+++ b/packages/houdini-svelte/src/runtime/stores/pagination/cursor.ts
@@ -49,9 +49,6 @@ export function cursorHandlers<_Data extends GraphQLObject, _Input>({
const config = await getConfig()
const client = await getCurrentClient()
- // set the loading state to true
- setFetching(true)
-
// build up the variables to pass to the query
const loadVariables: Record = {
...(await extraVariables?.()),
@@ -69,6 +66,7 @@ export function cursorHandlers<_Data extends GraphQLObject, _Input>({
artifact,
variables: loadVariables,
session: await getSession(),
+ setFetching,
cached: false,
config,
fetch,
@@ -218,9 +216,6 @@ export function cursorHandlers<_Data extends GraphQLObject, _Input>({
queryVariables[artifact.refetch!.update === 'prepend' ? 'last' : 'first'] = count
}
- // set the loading state to true
- setFetching(true)
-
// send the query
const result = await fetch({
...params,
diff --git a/packages/houdini-svelte/src/runtime/stores/pagination/offset.ts b/packages/houdini-svelte/src/runtime/stores/pagination/offset.ts
index 911beabfa6..64edecc58a 100644
--- a/packages/houdini-svelte/src/runtime/stores/pagination/offset.ts
+++ b/packages/houdini-svelte/src/runtime/stores/pagination/offset.ts
@@ -69,9 +69,6 @@ export function offsetHandlers<_Data extends GraphQLObject, _Input>({
throw missingPageSizeError('loadNextPage')
}
- // set the loading state to true
- setFetching(true)
-
// send the query
const { result } = await executeQuery({
client: await getCurrentClient(),
@@ -80,6 +77,7 @@ export function offsetHandlers<_Data extends GraphQLObject, _Input>({
session: await getSession(),
cached: false,
config,
+ setFetching,
fetch,
metadata,
})
@@ -127,9 +125,6 @@ export function offsetHandlers<_Data extends GraphQLObject, _Input>({
queryVariables.limit = count
}
- // set the loading state to true
- setFetching(true)
-
// send the query
const result = await fetch.call(this, {
...params,
diff --git a/packages/houdini-svelte/src/runtime/stores/query.ts b/packages/houdini-svelte/src/runtime/stores/query.ts
index e377a49ba6..ccff3505cc 100644
--- a/packages/houdini-svelte/src/runtime/stores/query.ts
+++ b/packages/houdini-svelte/src/runtime/stores/query.ts
@@ -35,7 +35,7 @@ export class QueryStore<
// identify it as a query store
kind = CompiledQueryKind
- // at its core, a query store is a writable store with extra methods{
+ // at its core, a query store is a writable store with extra methods
protected store: Writable>
// we will be reading and write the last known variables often, avoid frequent gets and updates
@@ -54,6 +54,14 @@ export class QueryStore<
// the string identifying the store
protected storeName: string
+ protected setFetching(isFetching: boolean) {
+ this.store?.update((s) => ({ ...s, isFetching }))
+ }
+
+ protected async currentVariables() {
+ return get(this.store).variables
+ }
+
constructor({ artifact, storeName, variables }: StoreConfig<_Data, _Input, QueryArtifact>) {
super()
@@ -73,8 +81,6 @@ export class QueryStore<
fetch(params?: QueryStoreFetchParams<_Data, _Input>): Promise>
async fetch(args?: QueryStoreFetchParams<_Data, _Input>): Promise> {
const config = await this.getConfig()
- // set the cache's config
- getCache().setConfig(config)
// validate and prepare the request context for the current environment (client vs server)
const { policy, params, context } = await fetchParams(this.artifact, this.storeName, args)
@@ -128,8 +134,6 @@ If this is leftovers from old versions of houdini, you can safely remove this \`
// we might not want to wait for the fetch to resolve
const fakeAwait = clientStarted && isBrowser && !params?.blocking
- this.setFetching(true)
-
// perform the network request
const request = this.fetchAndCache({
config,
@@ -161,10 +165,6 @@ If this is leftovers from old versions of houdini, you can safely remove this \`
return this.artifact.name
}
- protected async currentVariables() {
- return get(this.store).variables
- }
-
subscribe(
...args: Parameters>['subscribe']>
) {
@@ -226,6 +226,7 @@ If this is leftovers from old versions of houdini, you can safely remove this \`
const request = await fetchQuery<_Data, _Input>({
...context,
client: await getCurrentClient(),
+ setFetching: (val) => this.setFetching(val),
artifact,
variables,
cached,
@@ -342,15 +343,11 @@ If this is leftovers from old versions of houdini, you can safely remove this \`
this.lastVariables = newVariables
}
- protected setFetching(isFetching: boolean) {
- this.store?.update((s) => ({ ...s, isFetching }))
- }
-
private get initialState(): QueryResult<_Data, _Input> & _ExtraFields {
return {
data: null,
errors: null,
- isFetching: false,
+ isFetching: true,
partial: false,
source: null,
variables: {} as _Input,
@@ -443,7 +440,7 @@ import type { LoadEvent } from '@sveltejs/kit';
export async function load(${log.yellow('event')}: LoadEvent) {
return {
- ...load_MyQuery({ ${log.yellow('event')}, variables: { ... } })
+ ...load_${storeName}({ ${log.yellow('event')}, variables: { ... } })
};
}
`
diff --git a/packages/houdini/src/runtime/lib/network.ts b/packages/houdini/src/runtime/lib/network.ts
index 6520ce71c8..0022397bab 100644
--- a/packages/houdini/src/runtime/lib/network.ts
+++ b/packages/houdini/src/runtime/lib/network.ts
@@ -173,6 +173,7 @@ export async function executeQuery<_Data extends GraphQLObject, _Input extends {
artifact,
variables,
session,
+ setFetching,
cached,
fetch,
metadata,
@@ -181,6 +182,7 @@ export async function executeQuery<_Data extends GraphQLObject, _Input extends {
artifact: QueryArtifact | MutationArtifact
variables: _Input
session: any
+ setFetching: (fetching: boolean) => void
cached: boolean
config: ConfigFile
fetch?: typeof globalThis.fetch
@@ -194,6 +196,7 @@ export async function executeQuery<_Data extends GraphQLObject, _Input extends {
session,
},
artifact,
+ setFetching,
variables,
cached,
})
@@ -211,16 +214,18 @@ export async function executeQuery<_Data extends GraphQLObject, _Input extends {
export async function fetchQuery<_Data extends GraphQLObject, _Input extends {}>({
client,
+ context,
artifact,
variables,
+ setFetching,
cached = true,
policy,
- context,
}: {
client: HoudiniClient
context: FetchContext
artifact: QueryArtifact | MutationArtifact
variables: _Input
+ setFetching: (fetching: boolean) => void
cached?: boolean
policy?: CachePolicy
}): Promise> {
@@ -280,6 +285,9 @@ export async function fetchQuery<_Data extends GraphQLObject, _Input extends {}>
cache._internal_unstable.collectGarbage()
}, 0)
+ // tell everyone that we are fetching if the function is defined
+ setFetching(true)
+
// the request must be resolved against the network
const result = await client.sendRequest<_Data>(context, {
text: artifact.raw,