Open oodavid opened 1 year ago
I think this can be done today, without needing to do those checks. I think the issues is that the right side of the reactive assignment is still suggesting that user is a dependency.
<script lang="ts">
import { writable } from 'svelte/store';
function nameCheck(name: string) {
console.log('Called only when name changes');
return name;
}
function dobToAge(dob: number) {
console.log('Called only when dob changes');
var diff_ms = Date.now() - new Date(dob).getTime();
var age_dt = new Date(diff_ms);
return Math.abs(age_dt.getUTCFullYear() - 1970);
}
interface User {
name: string;
email: string;
dob: Date;
}
const user = writable<User>({
name: 'jane',
email: 'some@email.com',
dob: new Date('1990-01-01')
});
$: dobAsNum = +$user.dob; // evaluates when user changes
$: age = dobToAge(dobAsNum); // evaluate when dobAsNum changes
$: nameTracker = $user.name; // evaluates when user changes
$: name = nameCheck(nameTracker); // evaluates when nameTracker changes
</script>
<p>
Name:
{name}
</p>
<p>
{age}
</p>
<button
on:click={() =>
user.update((u) => {
let end = new Date('2000-01-01');
let start = new Date('1990-01-01');
const timeDiff = end.getTime() - start.getTime();
const randomTime = Math.random() * timeDiff;
const randomDate = new Date(start.getTime() + randomTime);
return { ...u, dob: randomDate };
})}>Change to random dob</button
>
<button on:click={() => user.update((u) => ({ ...u, name: u.name == 'jane' ? 'john' : 'jane' }))}
>Toggle name</button
>
note that I'm not tracking dob, as it will always emit when user changes, but it's numeric value.
The same can be achieved with stores
const dobAsNum = derived(user, user => +user.dob); // evaluates when user changes
const age = derived(dobAsNum, dobToAge); // evaluate when dobAsNum changes
if you want to extract some of this into a select fn.
function select<T, U extends Readable<T>>(store: U, prop: keyof T) {
return derived(store, (s) => s[prop]);
}
const nameTracker = select(user, 'name') // evaluates when user changes
$: name = nameCheck($nameTracker); // evaluates when nameTracker changes
But it will still have the same issues with derived values where typeof is 'object'
So for Dates you could
function selectDateAsNumber<T, U extends Readable<T>>(store: U, prop: keyof T) {
let isDate = s[prop] instanceof Date
if(!isDate) {
throw new Error("a Date was expected")
}
return derived(store, (s) => +s[prop]);
}
const dobAsNum = selectDateAsNumber(user, 'dob'); // evaluates when user changes
$: age = dobToAge($dobAsNum); // evaluate when dobAsNum changes
note: Only parts of the code have been tested.
Describe the problem
When a store exposes a complex object, we may want to selectively rebuild based on properties.
For example, a store may expose a User:
We may calculate our age from the date. If we only want to recalculate if the dob property has changed, we have to put extra checks in place.
...you could see how these checks could bloat with more complex logic
Describe the proposed solution
Have a way to
select
properties from stores, like so:NB, if #8469 was implemented, we could do this with a reactive store:
Alternatives considered
Once again, I'm taking inspiration from the Riverpod package for Dart / Flutter. It has a lot of similar goals and behaviours as svelte stores.
https://riverpod.dev/docs/concepts/reading#using-select-to-filter-rebuilds
Importance
would make my life easier