Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/two-mails-sip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@preact/signals-core": patch
---

- Fix a memory leak when computed signals and effects are removed
29 changes: 12 additions & 17 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
let ROOT: Signal;

/** This tracks subscriptions of signals read inside a computed */
let currentSignal: Signal;
let currentSignal: Signal | undefined;
let commitError: Error | null = null;

const pending = new Set<Signal>();
Expand Down Expand Up @@ -45,24 +43,24 @@ export class Signal<T = any> {
}

peek() {
if (currentSignal._canActivate && this._deps.size === 0) {
const shouldActivate = !currentSignal || currentSignal._canActivate;
if (shouldActivate && this._deps.size === 0) {
activate(this);
}
return this._value;
}

get value() {
const shouldActivate = !currentSignal || currentSignal._canActivate;
if (shouldActivate && this._deps.size === 0) {
activate(this);
}

// If we read a signal outside of a computed we have no way
// to unsubscribe from that. So we assume that the user wants
// to get the value immediately like for testing.
if (currentSignal._canActivate && this._deps.size === 0) {
activate(this);

// The ROOT signal cannot track dependencies as it's never
// subscribed to
if (currentSignal === ROOT) {
return this._value;
}
if (!currentSignal) {
return this._value;
}

// subscribe the current computed to this signal:
Expand All @@ -85,7 +83,7 @@ export class Signal<T = any> {

set value(value) {
if (this._readonly) {
throw new Error("Computed signals are readonly");
throw Error("Computed signals are readonly");
}

if (this._value !== value) {
Expand Down Expand Up @@ -180,7 +178,7 @@ function sweep(subs: Set<Signal<any>>) {

if (--signal._pending === 0) {
if (signal._isComputing) {
throw new Error("Cycle detected");
throw Error("Cycle detected");
}

signal._requiresUpdate = false;
Expand Down Expand Up @@ -248,9 +246,6 @@ function activate(signal: Signal) {
}
}

ROOT = currentSignal = new Signal(undefined);
ROOT._canActivate = true;

export function signal<T>(value: T): Signal<T> {
return new Signal(value);
}
Expand Down