Skip to content

Commit 5b359b3

Browse files
authored
fix: expose query.set (#14303)
Exposes the .set() method on remote queries. Fixes #14302
1 parent 2333f25 commit 5b359b3

File tree

7 files changed

+59
-3
lines changed

7 files changed

+59
-3
lines changed

.changeset/social-hats-hope.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: add type definitions for `query.set()` method to override the value of a remote query function

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

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,17 +160,40 @@ export const getPost = query(v.string(), async (slug) => {
160160
161161
Both the argument and the return value are serialized with [devalue](https://github.com/sveltejs/devalue), which handles types like `Date` and `Map` (and custom types defined in your [transport hook](hooks#Universal-hooks-transport)) in addition to JSON.
162162
163-
### Refreshing queries
163+
### Updating queries
164164
165-
Any query can be updated via its `refresh` method:
165+
Any query can be re-fetched via its `refresh` method, which retrieves the latest value from the server:
166166
167167
```svelte
168168
<button onclick={() => getPosts().refresh()}>
169169
Check for new posts
170170
</button>
171171
```
172172
173-
> [!NOTE] Queries are cached while they're on the page, meaning `getPosts() === getPosts()`. This means you don't need a reference like `const posts = getPosts()` in order to refresh the query.
173+
Alternatively, if you need to update its value manually, you can use the `set` method:
174+
175+
```svelte
176+
<script>
177+
import { getPosts } from './data.remote';
178+
import { onMount } from 'svelte';
179+
180+
onMount(() => {
181+
const ws = new WebSocket('/ws');
182+
ws.addEventListener('message', (ev) => {
183+
const message = JSON.parse(ev.data);
184+
if (message.type === 'new-post') {
185+
getPosts().set([
186+
message.post,
187+
...getPosts().current,
188+
]);
189+
}
190+
});
191+
return () => ws.close();
192+
});
193+
</script>
194+
```
195+
196+
> [!NOTE] Queries are cached while they're on the page, meaning `getPosts() === getPosts()`. This means you don't need a reference like `const posts = getPosts()` in order to update the query.
174197
175198
## form
176199

packages/kit/src/exports/public.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1802,6 +1802,12 @@ export type RemoteResource<T> = Promise<Awaited<T>> & {
18021802
);
18031803

18041804
export type RemoteQuery<T> = RemoteResource<T> & {
1805+
/**
1806+
* On the client, this function will update the value of the query without re-fetching it.
1807+
*
1808+
* On the server, this throws an error.
1809+
*/
1810+
set(value: T): void;
18051811
/**
18061812
* On the client, this function will re-fetch the query from the server.
18071813
*

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ export function query(validate_or_fn, maybe_fn) {
7979

8080
promise.catch(() => {});
8181

82+
promise.set = () => {
83+
throw new Error(`Cannot call '${__.name}.set()' on the server`);
84+
};
85+
8286
promise.refresh = async () => {
8387
const { state } = get_request_store();
8488
const refreshes = state.refreshes;

packages/kit/test/apps/basics/src/routes/remote/+page.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737

3838
<button onclick={() => count.refresh()} id="refresh-btn">Refresh</button>
3939

40+
<button onclick={() => count.set(999)} id="set-btn">Set</button>
41+
4042
<button
4143
onclick={async () => {
4244
command_result = await set_count({ c: 2 });

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,6 +1660,16 @@ test.describe('remote functions', () => {
16601660
}
16611661
});
16621662

1663+
test('query.set works', async ({ page }) => {
1664+
await page.goto('/remote');
1665+
let request_count = 0;
1666+
page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0));
1667+
1668+
await page.click('#set-btn');
1669+
await expect(page.locator('#count-result')).toHaveText('999 / 999 (false)');
1670+
expect(request_count).toBe(0);
1671+
});
1672+
16631673
test('hydrated data is reused', async ({ page }) => {
16641674
let request_count = 0;
16651675
page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0));

packages/kit/types/index.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1778,6 +1778,12 @@ declare module '@sveltejs/kit' {
17781778
);
17791779

17801780
export type RemoteQuery<T> = RemoteResource<T> & {
1781+
/**
1782+
* On the client, this function will update the value of the query without re-fetching it.
1783+
*
1784+
* On the server, this throws an error.
1785+
*/
1786+
set(value: T): void;
17811787
/**
17821788
* On the client, this function will re-fetch the query from the server.
17831789
*

0 commit comments

Comments
 (0)