Skip to content

Commit 92415a7

Browse files
committed
fix
1 parent 50546dd commit 92415a7

File tree

3 files changed

+66
-7
lines changed

3 files changed

+66
-7
lines changed

.changeset/wicked-zebras-exist.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: ensure SvelteDate cached methods have correct reactive context

packages/svelte/src/reactivity/date.js

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,35 @@
1-
/** @import { Source } from '#client' */
2-
import { without_reactive_context } from '../internal/client/dom/elements/bindings/shared.js';
1+
/** @import { Source, Derived, Effect } from '#client' */
2+
import { DERIVED } from '../internal/client/constants.js';
33
import { derived } from '../internal/client/index.js';
44
import { source, set } from '../internal/client/reactivity/sources.js';
5-
import { get } from '../internal/client/runtime.js';
5+
import { active_reaction, get, set_active_reaction } from '../internal/client/runtime.js';
66

77
var inited = false;
88

9+
/**
10+
* @template T
11+
* @param {() => T} fn
12+
* @returns {T}
13+
*/
14+
function with_parent_effect(fn) {
15+
var previous_reaction = active_reaction;
16+
var parent_reaction = active_reaction;
17+
18+
while (parent_reaction !== null) {
19+
if ((parent_reaction.f & DERIVED) === 0) {
20+
break;
21+
}
22+
parent_reaction = /** @type {Derived | Effect} */ (parent_reaction).parent;
23+
}
24+
25+
set_active_reaction(parent_reaction);
26+
try {
27+
return fn();
28+
} finally {
29+
set_active_reaction(previous_reaction);
30+
}
31+
}
32+
933
export class SvelteDate extends Date {
1034
#time = source(super.getTime());
1135

@@ -44,10 +68,10 @@ export class SvelteDate extends Date {
4468
var d = this.#deriveds.get(method);
4569

4670
if (d === undefined) {
47-
// We don't want to associate the derived with the current
48-
// reactive context, as that means the derived will get destroyed
49-
// each time it re-fires
50-
d = without_reactive_context(() =>
71+
// Ensure we create the derived inside the nearest parent effect if
72+
// we're inside a derived, otherwise the derived will be destroyed each
73+
// time it re-runs
74+
d = with_parent_effect(() =>
5175
derived(() => {
5276
get(this.#time);
5377
// @ts-ignore

packages/svelte/src/reactivity/date.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,3 +642,33 @@ test('Date methods invoked for the first time in a derived', () => {
642642

643643
cleanup();
644644
});
645+
646+
test('Date methods shared between deriveds', () => {
647+
const date = new SvelteDate(initial_date);
648+
const log: any = [];
649+
650+
const cleanup = effect_root(() => {
651+
const year = derived(() => {
652+
return date.getFullYear();
653+
});
654+
const year2 = derived(() => {
655+
return (date.getTime(), date.getFullYear())
656+
});
657+
658+
render_effect(() => {
659+
log.push(get(year) + '/' + get(year2).toString());
660+
});
661+
662+
flushSync(() => {
663+
date.setFullYear(date.getFullYear() + 1);
664+
});
665+
666+
flushSync(() => {
667+
date.setFullYear(date.getFullYear() + 1);
668+
});
669+
});
670+
671+
assert.deepEqual(log, ['2023/2023', '2024/2024', '2025/2025']);
672+
673+
cleanup();
674+
});

0 commit comments

Comments
 (0)