pull/18573/merge
Bored.gyl 5 days ago committed by GitHub
commit f30b9049e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -62,6 +62,86 @@ let total = $derived(await a + b);
...both `a` and `b` are tracked, even though `b` is only read once `a` has resolved, after the initial execution. (This does not apply to `await` in functions that are called by the expression, only the expression itself.) ...both `a` and `b` are tracked, even though `b` is only read once `a` has resolved, after the initial execution. (This does not apply to `await` in functions that are called by the expression, only the expression itself.)
On the other hand, values that are read _asynchronously_ (such as inside asynchronous closures passed to third-party query or observable libraries) will not be tracked automatically. To track these values, read them synchronously outside the asynchronous boundary. A clear pattern for this is to evaluate the dependencies using the `void` operator:
```js
// @filename: ambient.d.ts
declare module 'dexie' {
export class Dexie {
constructor(databaseName: string);
users: {
where(key: string): {
between(min: number, max: number): {
toArray(): Promise<any[]>;
};
};
};
}
export function liveQuery<T>(querier: () => T | Promise<T>): any;
}
// @filename: index.js
// ---cut---
import { Dexie, liveQuery } from 'dexie';
const db = new Dexie('UserDatabase');
// ...database setup...
let minAge = $state(18);
let maxAge = $state(30);
let query = $derived.by(() => {
+++// Read dependencies synchronously so they are tracked+++
+++void minAge, maxAge;+++
return liveQuery(async () => {
// Values read inside this callback are not tracked automatically
return await db.users
.where('age')
.between(minAge, maxAge)
.toArray();
});
});
```
Alternatively, you can extract the asynchronous callback into a separate function and pass the reactive values as arguments, which are evaluated synchronously:
```js
// @filename: ambient.d.ts
declare module 'dexie' {
export class Dexie {
constructor(databaseName: string);
users: {
where(key: string): {
between(min: number, max: number): {
toArray(): Promise<any[]>;
};
};
};
}
export function liveQuery<T>(querier: () => T | Promise<T>): any;
}
// @filename: index.js
import { Dexie, liveQuery } from 'dexie';
const db = new Dexie('UserDatabase');
let minAge = $state(18);
let maxAge = $state(30);
// ---cut---
+++function createUserQuery(minAge, maxAge) {+++
return liveQuery(async () => {
return await db.users
.where('age')
.between(minAge, maxAge)
.toArray();
});
}
// minAge and maxAge are read synchronously when the function is called
let query = $derived(+++createUserQuery(minAge, maxAge)+++);
```
To exempt a piece of state from being treated as a dependency, use [`untrack`](svelte#untrack). To exempt a piece of state from being treated as a dependency, use [`untrack`](svelte#untrack).
## Overriding derived values ## Overriding derived values

@ -107,6 +107,57 @@ $effect(() => {
}); });
``` ```
To force the `$effect` to track the values read _asynchronously_, read them synchronously outside the asynchronous boundary. A clear pattern for this is to evaluate the dependencies using the `void` operator:
```ts
// @filename: index.ts
declare let canvas: {
width: number;
height: number;
getContext(type: '2d', options?: CanvasRenderingContext2DSettings): CanvasRenderingContext2D;
};
declare let size: number;
// ---cut---
$effect(() => {
const context = canvas.getContext('2d');
+++// read `size` synchronously so it is registered as a dependency+++
+++void size;+++
setTimeout(() => {
// ...and now this will also re-run when `size` changes
context.fillRect(0, 0, size, size);
}, 0);
});
```
Alternatively, you can extract the asynchronous logic into a separate function and pass the reactive values as arguments, which are evaluated synchronously:
```ts
// @filename: index.ts
declare let canvas: {
width: number;
height: number;
getContext(type: '2d', options?: CanvasRenderingContext2DSettings): CanvasRenderingContext2D;
};
declare let size: number;
// ---cut---
$effect(() => {
const context = canvas.getContext('2d');
+++const draw = (size: number) => {+++
setTimeout(() => {
context.fillRect(0, 0, size, size);
}, 0);
};
// size is read synchronously when the function is called
+++draw(size);+++
});
```
An effect only reruns when the object it reads changes, not when a property inside it changes. (If you want to observe changes _inside_ an object at dev time, you can use [`$inspect`]($inspect).) An effect only reruns when the object it reads changes, not when a property inside it changes. (If you want to observe changes _inside_ an object at dev time, you can use [`$inspect`]($inspect).)
```svelte ```svelte

Loading…
Cancel
Save