Skip to content

Commit 4e281a5

Browse files
committed
fix: visit the derived function when to update the dependencies even when it's reassigned
1 parent 81d972f commit 4e281a5

File tree

4 files changed

+41
-13
lines changed

4 files changed

+41
-13
lines changed

packages/svelte/src/internal/client/constants.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@ export const DISCONNECTED = 1 << 9;
1010
export const CLEAN = 1 << 10;
1111
export const DIRTY = 1 << 11;
1212
export const MAYBE_DIRTY = 1 << 12;
13-
export const INERT = 1 << 13;
14-
export const DESTROYED = 1 << 14;
15-
export const EFFECT_RAN = 1 << 15;
13+
export const ASSIGNED_DERIVED = 1 << 13;
14+
export const INERT = 1 << 14;
15+
export const DESTROYED = 1 << 15;
16+
export const EFFECT_RAN = 1 << 16;
1617
/** 'Transparent' effects do not create a transition boundary */
17-
export const EFFECT_TRANSPARENT = 1 << 16;
18+
export const EFFECT_TRANSPARENT = 1 << 17;
1819
/** Svelte 4 legacy mode props need to be handled with deriveds and be recognized elsewhere, hence the dedicated flag */
19-
export const LEGACY_DERIVED_PROP = 1 << 17;
20-
export const INSPECT_EFFECT = 1 << 18;
21-
export const HEAD_EFFECT = 1 << 19;
22-
export const EFFECT_HAS_DERIVED = 1 << 20;
23-
export const EFFECT_IS_UPDATING = 1 << 21;
20+
export const LEGACY_DERIVED_PROP = 1 << 18;
21+
export const INSPECT_EFFECT = 1 << 19;
22+
export const HEAD_EFFECT = 1 << 20;
23+
export const EFFECT_HAS_DERIVED = 1 << 21;
24+
export const EFFECT_IS_UPDATING = 1 << 22;
2425

2526
export const STATE_SYMBOL = Symbol('$state');
2627
export const STATE_SYMBOL_METADATA = Symbol('$state metadata');

packages/svelte/src/internal/client/reactivity/sources.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
MAYBE_DIRTY,
3030
BLOCK_EFFECT,
3131
ROOT_EFFECT,
32-
EFFECT_IS_UPDATING
32+
ASSIGNED_DERIVED
3333
} from '../constants.js';
3434
import * as e from '../errors.js';
3535
import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js';
@@ -172,7 +172,11 @@ export function internal_set(source, value) {
172172
}
173173

174174
if ((source.f & DERIVED) !== 0) {
175-
set_signal_status(source, (source.f & UNOWNED) === 0 ? CLEAN : MAYBE_DIRTY);
175+
// if we are assigning a derived we set it to clean but we also
176+
// keep the assigned derived flag so that we can read the dependencies
177+
// in the get function
178+
var flags = (source.f & UNOWNED) === 0 ? CLEAN : MAYBE_DIRTY;
179+
set_signal_status(source, flags | ASSIGNED_DERIVED);
176180
}
177181

178182
mark_reactions(source, DIRTY);

packages/svelte/src/internal/client/runtime.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import {
2323
LEGACY_DERIVED_PROP,
2424
DISCONNECTED,
2525
BOUNDARY_EFFECT,
26-
EFFECT_IS_UPDATING
26+
EFFECT_IS_UPDATING,
27+
ASSIGNED_DERIVED
2728
} from './constants.js';
2829
import { flush_tasks } from './dom/task.js';
2930
import { internal_set, old_values } from './reactivity/sources.js';
@@ -926,6 +927,11 @@ export function get(signal) {
926927

927928
if (check_dirtiness(derived)) {
928929
update_derived(derived);
930+
} else if ((derived.f & ASSIGNED_DERIVED) !== 0) {
931+
// if the derived is clean but has been assigned a new value, then we need to
932+
// still invoke the derived function and update the dependencies or else
933+
// it will not depend on the original source
934+
update_reaction(derived);
929935
}
930936
}
931937

@@ -1043,7 +1049,7 @@ export function untrack(fn) {
10431049
}
10441050
}
10451051

1046-
const STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN);
1052+
const STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN | ASSIGNED_DERIVED);
10471053

10481054
/**
10491055
* @param {Signal} signal

packages/svelte/tests/signals/test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,12 +1089,29 @@ describe('signals', () => {
10891089
assert.equal($.get(b), 1);
10901090

10911091
set(a, 2);
1092+
assert.equal($.get(b), 2);
10921093
set(b, 3);
10931094

10941095
assert.equal($.get(b), 3);
10951096
};
10961097
});
10971098

1099+
test("unowned deriveds set after they are DIRTY doesn't get updated on get", () => {
1100+
return () => {
1101+
const a = state(0);
1102+
const b = derived(() => $.get(a));
1103+
const c = derived(() => $.get(b));
1104+
1105+
set(b, 1);
1106+
assert.equal($.get(c), 1);
1107+
1108+
set(a, 2);
1109+
1110+
assert.equal($.get(b), 2);
1111+
assert.equal($.get(c), 2);
1112+
};
1113+
});
1114+
10981115
test('deriveds containing effects work correctly when used with untrack', () => {
10991116
return () => {
11001117
let a = render_effect(() => {});

0 commit comments

Comments
 (0)