Skip to content

Commit 2b254bc

Browse files
alxhubleonsenft
authored andcommitted
fix(core): linkedSignal.update should propagate errors
Unlike a normal `signal()`, a `linkedSignal()` can be in an error state when its computation fails. Currently, there's a bug where `linkedSignal.update` does not account for this error state, and will pass the internal `ERRORED` `Symbol` as the current value to the updater function. This commit fixes the issue by having `update()` check and throw the error instead of calling the updater function. (cherry picked from commit aff9e36)
1 parent e5110b4 commit 2b254bc

2 files changed

Lines changed: 25 additions & 0 deletions

File tree

packages/core/primitives/signals/src/linked_signal.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ export function linkedSignalUpdateFn<S, D>(
113113
updater: (value: D) => D,
114114
): void {
115115
producerUpdateValueVersion(node);
116+
// update() on a linked signal can't work if the current state is ERRORED, as there's no value.
117+
if (node.value === ERRORED) {
118+
throw node.error;
119+
}
116120
signalUpdateFn(node, updater);
117121
producerMarkClean(node);
118122
}

packages/core/test/signals/linked_signal_spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,27 @@ describe('linkedSignal', () => {
160160
expect(choice()).toBe(0);
161161
});
162162

163+
it('should throw error from update if current computation state is an error', () => {
164+
const source = linkedSignal<number>(() => {
165+
// Initial computation fails.
166+
throw new Error('failure');
167+
});
168+
169+
let updaterRan = false;
170+
expect(() =>
171+
source.update(() => {
172+
// Note: we explicitly do _not_ interact with the previous value here. That's because in the
173+
// failure mode, the previous value is an internal `Symbol` from `linkedSignal`, and we want
174+
// to avoid throwing any errors related to this `Symbol` and causing the test to incorrectly
175+
// pass.
176+
177+
updaterRan = true;
178+
return 0;
179+
}),
180+
).toThrowError(/failure/);
181+
expect(updaterRan).toBeFalse();
182+
});
183+
163184
it('should not recompute downstream dependencies when computed value is equal to the currently set value', () => {
164185
const source = signal(0);
165186
const isEven = linkedSignal(() => source() % 2 === 0);

0 commit comments

Comments
 (0)