Skip to content

Commit 9493537

Browse files
authored
fix: don't refresh queries automatically when running commands (#14170)
* fix: don't refresh queries automatically when running commands * update docs * unused * DRY out, fix type error
1 parent e0cda3a commit 9493537

File tree

8 files changed

+71
-56
lines changed

8 files changed

+71
-56
lines changed

.changeset/upset-dots-change.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: don't refresh queries automatically when running commands

documentation/docs/20-core-concepts/60-remote-functions.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -503,9 +503,11 @@ Now simply call `addLike`, from (for example) an event handler:
503503
504504
> [!NOTE] Commands cannot be called during render.
505505
506-
### Single-flight mutations
506+
### Updating queries
507+
508+
To update `getLikes(item.id)`, or any other query, we need to tell SvelteKit _which_ queries need to be refreshed (unlike `form`, which by default invalidates everything, to approximate the behaviour of a native form submission).
507509
508-
As with forms, any queries on the page (such as `getLikes(item.id)` in the example above) will automatically be refreshed following a successful command. But we can make things more efficient by telling SvelteKit which queries will be affected by the command, either inside the command itself...
510+
We either do that inside the command itself...
509511
510512
```js
511513
/// file: likes.remote.js

packages/kit/src/runtime/client/remote-functions/command.svelte.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,11 @@ export function command(id) {
6161
release_overrides(updates);
6262
throw new HttpError(result.status ?? 500, result.error);
6363
} else {
64-
refresh_queries(result.refreshes, updates);
64+
console.log('refreshes', result.refreshes);
65+
66+
if (result.refreshes) {
67+
refresh_queries(result.refreshes, updates);
68+
}
6569

6670
return devalue.parse(result.result, app.decoders);
6771
}

packages/kit/src/runtime/client/remote-functions/form.svelte.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ import { app_dir, base } from '__sveltekit/paths';
55
import * as devalue from 'devalue';
66
import { DEV } from 'esm-env';
77
import { HttpError } from '@sveltejs/kit/internal';
8-
import { app, remote_responses, started, goto, set_nearest_error_page } from '../client.js';
8+
import {
9+
app,
10+
remote_responses,
11+
started,
12+
goto,
13+
set_nearest_error_page,
14+
invalidateAll
15+
} from '../client.js';
916
import { tick } from 'svelte';
1017
import { refresh_queries, release_overrides } from './shared.svelte.js';
1118

@@ -84,7 +91,11 @@ export function form(id) {
8491
if (form_result.type === 'result') {
8592
result = devalue.parse(form_result.result, app.decoders);
8693

87-
refresh_queries(form_result.refreshes, updates);
94+
if (form_result.refreshes) {
95+
refresh_queries(form_result.refreshes, updates);
96+
} else {
97+
void invalidateAll();
98+
}
8899
} else if (form_result.type === 'redirect') {
89100
const refreshes = form_result.refreshes ?? '';
90101
const invalidateAll = !refreshes && updates.length === 0;

packages/kit/src/runtime/client/remote-functions/shared.svelte.js

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
/** @import { RemoteFunctionResponse } from 'types' */
33
/** @import { Query } from './query.svelte.js' */
44
import * as devalue from 'devalue';
5-
import { app, goto, invalidateAll, query_map } from '../client.js';
5+
import { app, goto, query_map } from '../client.js';
66
import { HttpError, Redirect } from '@sveltejs/kit/internal';
77
import { tick } from 'svelte';
88
import { create_remote_cache_key, stringify_remote_arg } from '../../shared.js';
@@ -125,19 +125,16 @@ export function release_overrides(updates) {
125125
*/
126126
export function refresh_queries(stringified_refreshes, updates = []) {
127127
const refreshes = Object.entries(devalue.parse(stringified_refreshes, app.decoders));
128-
if (refreshes.length > 0) {
129-
// `refreshes` is a superset of `updates`
130-
for (const [key, value] of refreshes) {
131-
// If there was an optimistic update, release it right before we update the query
132-
const update = updates.find((u) => u._key === key);
133-
if (update && 'release' in update) {
134-
update.release();
135-
}
136-
// Update the query with the new value
137-
const entry = query_map.get(key);
138-
entry?.resource.set(value);
128+
129+
// `refreshes` is a superset of `updates`
130+
for (const [key, value] of refreshes) {
131+
// If there was an optimistic update, release it right before we update the query
132+
const update = updates.find((u) => u._key === key);
133+
if (update && 'release' in update) {
134+
update.release();
139135
}
140-
} else {
141-
void invalidateAll();
136+
// Update the query with the new value
137+
const entry = query_map.get(key);
138+
entry?.resource.set(value);
142139
}
143140
}

packages/kit/src/runtime/server/remote.js

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,7 @@ export async function handle_remote_call(event, options, manifest, id) {
6262
/** @type {RemoteFunctionResponse} */ ({
6363
type: 'result',
6464
result: stringify(data, transport),
65-
refreshes: stringify(
66-
{
67-
...get_event_state(event).refreshes,
68-
...(await apply_client_refreshes(/** @type {string[]} */ (form_client_refreshes)))
69-
},
70-
transport
71-
)
65+
refreshes: await serialize_refreshes(/** @type {string[]} */ (form_client_refreshes))
7266
})
7367
);
7468
}
@@ -78,13 +72,12 @@ export async function handle_remote_call(event, options, manifest, id) {
7872
const { payload, refreshes } = await event.request.json();
7973
const arg = parse_remote_arg(payload, transport);
8074
const data = await with_event(event, () => fn(arg));
81-
const refreshed = await apply_client_refreshes(refreshes);
8275

8376
return json(
8477
/** @type {RemoteFunctionResponse} */ ({
8578
type: 'result',
8679
result: stringify(data, transport),
87-
refreshes: stringify({ ...get_event_state(event).refreshes, ...refreshed }, transport)
80+
refreshes: await serialize_refreshes(refreshes)
8881
})
8982
);
9083
}
@@ -107,14 +100,10 @@ export async function handle_remote_call(event, options, manifest, id) {
107100
);
108101
} catch (error) {
109102
if (error instanceof Redirect) {
110-
const refreshes = {
111-
...(get_event_state(event).refreshes ?? {}), // could be set by form actions
112-
...(await apply_client_refreshes(form_client_refreshes ?? []))
113-
};
114103
return json({
115104
type: 'redirect',
116105
location: error.location,
117-
refreshes: Object.keys(refreshes).length > 0 ? stringify(refreshes, transport) : undefined
106+
refreshes: await serialize_refreshes(form_client_refreshes ?? [])
118107
});
119108
}
120109

@@ -132,26 +121,33 @@ export async function handle_remote_call(event, options, manifest, id) {
132121
);
133122
}
134123

135-
/** @param {string[]} refreshes */
136-
async function apply_client_refreshes(refreshes) {
137-
return Object.fromEntries(
138-
await Promise.all(
139-
refreshes.map(async (key) => {
140-
const [hash, name, payload] = key.split('/');
141-
const loader = manifest._.remotes[hash];
142-
143-
// TODO what do we do in this case? erroring after the mutation has happened is not great
144-
if (!loader) error(400, 'Bad Request');
145-
146-
const module = await loader();
147-
const fn = module[name];
148-
149-
if (!fn) error(400, 'Bad Request');
150-
151-
return [key, await with_event(event, () => fn(parse_remote_arg(payload, transport)))];
152-
})
124+
/**
125+
* @param {string[]} client_refreshes
126+
*/
127+
async function serialize_refreshes(client_refreshes) {
128+
const refreshes = {
129+
...get_event_state(event).refreshes,
130+
...Object.fromEntries(
131+
await Promise.all(
132+
client_refreshes.map(async (key) => {
133+
const [hash, name, payload] = key.split('/');
134+
const loader = manifest._.remotes[hash];
135+
136+
// TODO what do we do in this case? erroring after the mutation has happened is not great
137+
if (!loader) error(400, 'Bad Request');
138+
139+
const module = await loader();
140+
const fn = module[name];
141+
142+
if (!fn) error(400, 'Bad Request');
143+
144+
return [key, await with_event(event, () => fn(parse_remote_arg(payload, transport)))];
145+
})
146+
)
153147
)
154-
);
148+
};
149+
150+
return Object.keys(refreshes).length > 0 ? stringify(refreshes, transport) : undefined;
155151
}
156152
}
157153

packages/kit/src/types/internal.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ export type RemoteFunctionResponse =
302302
type: 'result';
303303
result: string;
304304
/** devalue'd Record<string, any> */
305-
refreshes: string;
305+
refreshes: string | undefined;
306306
};
307307

308308
/**

packages/kit/test/apps/basics/test/client.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,7 +1664,7 @@ test.describe('remote functions', () => {
16641664
expect(request_count).toBe(2);
16651665
});
16661666

1667-
test('command returns correct sum and refreshes all data by default', async ({ page }) => {
1667+
test('command returns correct sum but does not refresh data by default', async ({ page }) => {
16681668
await page.goto('/remote');
16691669
await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)');
16701670

@@ -1673,9 +1673,9 @@ test.describe('remote functions', () => {
16731673

16741674
await page.click('#multiply-btn');
16751675
await expect(page.locator('#command-result')).toHaveText('2');
1676-
await expect(page.locator('#count-result')).toHaveText('2 / 2 (false)');
1676+
await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)');
16771677
await page.waitForTimeout(100); // allow all requests to finish
1678-
expect(request_count).toBe(4); // 1 for the command, 3 for the refresh
1678+
expect(request_count).toBe(1); // 1 for the command, no refreshes
16791679
});
16801680

16811681
test('command returns correct sum and does client-initiated single flight mutation', async ({

0 commit comments

Comments
 (0)