diff --git a/.changeset/atom-set-undefined.md b/.changeset/atom-set-undefined.md new file mode 100644 index 00000000..cdaab220 --- /dev/null +++ b/.changeset/atom-set-undefined.md @@ -0,0 +1,5 @@ +--- +'@tanstack/store': patch +--- + +Fix `atom.set(undefined)` being ignored. A mutable atom now takes `undefined` as its new value and compares it like any other, so setting `undefined` again does not notify subscribers. Only an internal `_update()` call made with no argument is still skipped. diff --git a/packages/store/src/atom.ts b/packages/store/src/atom.ts index 1a29e607..be69b62e 100644 --- a/packages/store/src/atom.ts +++ b/packages/store/src/atom.ts @@ -203,9 +203,10 @@ export function createAtom( activeSub = atom ++cycle atom.depsTail = undefined - } else if (getValue === undefined) { + } else if (arguments.length === 0) { // Mutable atoms can be marked dirty by the reactive graph, but they should - // never be recomputed without an explicit value/updater. + // never be recomputed without an explicit value/updater. Check the argument + // count rather than `undefined`, which `set(undefined)` passes as a value. return false } if (isComputed) { @@ -219,7 +220,12 @@ export function createAtom( : getValue === undefined && isComputed ? getter(oldValue) : getValue! - if (oldValue === undefined || !compare(oldValue, newValue)) { + // A computed atom treats an `undefined` snapshot as not computed yet. A + // mutable atom's `undefined` is a value like any other. + if ( + (isComputed && oldValue === undefined) || + !compare(oldValue, newValue) + ) { atom._snapshot = newValue return true } diff --git a/packages/store/tests/store.test.ts b/packages/store/tests/store.test.ts index 7c088144..f1b17b1b 100644 --- a/packages/store/tests/store.test.ts +++ b/packages/store/tests/store.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test, vi } from 'vitest' // import { Store } from '../src/index' -import { createStore } from '../src' +import { createAtom, createStore } from '../src' describe('store', () => { test(`should set the initial value`, () => { @@ -116,4 +116,17 @@ describe('store', () => { expect(atom._update()).toBe(false) expect(store.state).toEqual({ pageIndex: 1 }) }) + + test('mutable atom can be set to undefined', () => { + const atom = createAtom(1) + const fn = vi.fn() + atom.subscribe(fn) + + atom.set(undefined) + atom.set(undefined) + + expect(atom.get()).toBeUndefined() + expect(fn).toHaveBeenCalledTimes(1) + expect(fn).toHaveBeenCalledWith(undefined) + }) })