chem-rx 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0 - 2026-06-14
4
+
5
+ ### Added
6
+
7
+ - `useSelector(atom, selector, equals?)` React hook (`chem-rx/react`): derive any
8
+ value from an atom with a selector function and re-render only when the selected
9
+ value changes. The optional `equals` comparator (defaults to `Object.is`) dedupes
10
+ re-renders by content, so an atom that churns references on every update only
11
+ re-renders the component when the selected slice is not equal. See
12
+ [useSelector](./README.md#useselector).
13
+
14
+ ### Removed (breaking)
15
+
16
+ - Removed `useSelectAtom`. Migrate to `useSelector` with a selector function:
17
+ `useSelectAtom(atom, 'key')` becomes `useSelector(atom, (s) => s.key)`.
18
+
19
+ ## 0.2.0 - 2026-06-13
20
+
21
+ ### Added
22
+
23
+ - Optional `equals` comparator on every atom creation path: `Atom(value, { equals })`,
24
+ `atom.derive(fn, { equals })`, and `atom.select(key, { equals })`. Supply a
25
+ content comparator to dedup notifications by data instead of identity. See
26
+ [Equality & change detection](./README.md#equality--change-detection).
27
+ - `Atom()`'s second argument now also accepts an options object
28
+ (`{ readOnly?, equals? }`) in addition to the legacy `readOnly` boolean.
29
+ - Exported `Equals<T>` and `AtomFactoryOptions<T>` types.
30
+
31
+ ### Changed (breaking)
32
+
33
+ - Every atom now decides whether to notify subscribers using an equality
34
+ comparator that **defaults to `Object.is`**:
35
+ - A base atom no longer re-emits when `next()` is called with an
36
+ `Object.is`-equal value (previously every `next()` notified).
37
+ - `derive` no longer re-emits when its computed output is `Object.is`-equal
38
+ (previously it re-emitted on every parent update).
39
+ - `select` and `combine` are unchanged in practice (already `Object.is`).
40
+
41
+ Migration: pass `equals: () => false` to restore the previous always-notify
42
+ behavior.
package/README.md CHANGED
@@ -3,6 +3,14 @@
3
3
  `chem-rx` provides a small atomic state management layer focused on simplicity.
4
4
  Useable with or without React!
5
5
 
6
+ > **Breaking change (unreleased):** atoms now decide whether to notify
7
+ > subscribers using an equality comparator that **defaults to `Object.is`**.
8
+ > Two behaviors changed: a base atom no longer re-emits when you set an
9
+ > `Object.is`-equal value, and `derive` no longer re-emits when its computed
10
+ > output is `Object.is`-equal (it previously always re-emitted on every parent
11
+ > update). To restore the old always-notify behavior, pass `equals: () => false`.
12
+ > See [Equality & change detection](#equality--change-detection).
13
+
6
14
  ## Atom
7
15
 
8
16
  `chem-rx` is an atomic approach to state management, similar to
@@ -90,6 +98,14 @@ This can be especially useful for working with different parts of nested Array a
90
98
  Atoms created with `select` are **read-only** (`ReadOnlyAtom`). This prevents you from modifying original values that the atom was created from.
91
99
  Selected atoms are lazy: calling `value()` reads the latest parent snapshot, and subscribing to a selected atom listens to the parent only while that selected atom has active subscribers.
92
100
 
101
+ By default a selected atom notifies subscribers only when the selected value changes by `Object.is`. Pass `{ equals }` to dedup by content instead (see [Equality & change detection](#equality--change-detection)):
102
+
103
+ ```
104
+ const stacy = students.select("stacy", {
105
+ equals: (a, b) => a?.nickname === b?.nickname,
106
+ });
107
+ ```
108
+
93
109
  ```
94
110
  const students = Atom({
95
111
  stacy: {
@@ -149,19 +165,36 @@ squared$.value() // "16"
149
165
  squared$.set(2)
150
166
  ```
151
167
 
152
- You can optionally enforce `readOnly` on an atom at creation time if needed
168
+ By default, a derived atom notifies subscribers only when its computed output changes by `Object.is`. When `deriveFn` returns a fresh object/array each time (so `Object.is` never matches), pass `{ equals }` to dedup by content (see [Equality & change detection](#equality--change-detection)):
169
+
170
+ ```
171
+ const items$ = data$.derive((data) => data.items, {
172
+ equals: (a, b) => a.length === b.length && a.every((x, i) => x.id === b[i].id),
173
+ });
174
+ ```
175
+
176
+ You can optionally enforce `readOnly` on an atom at creation time if needed. The
177
+ second argument also accepts an options object (`{ readOnly?, equals? }`):
153
178
 
154
179
  ```
155
180
  const atom$ = Atom(3, true);
181
+ // equivalent:
182
+ const atom2$ = Atom(3, { readOnly: true });
156
183
 
157
184
  // ERR: Property 'set' does not exist on type ReadOnlyAtom
158
185
  atom$.set(2)
186
+
187
+ // content-based dedup on a writable atom
188
+ const point$ = Atom({ x: 0, y: 0 }, { equals: (a, b) => a.x === b.x && a.y === b.y });
159
189
  ```
160
190
 
161
191
  ### Combining Atoms
162
192
 
163
193
  Multiple atoms can also be **combined** to create brand new atoms.
164
194
 
195
+ `combine` produces a fresh array snapshot on every input change, so it re-emits on
196
+ every update. To dedup by content, chain a `derive(fn, { equals })` onto it.
197
+
165
198
  Here's an example of joining a set of normalized data models
166
199
 
167
200
  ```
@@ -198,8 +231,14 @@ console.log(mary$.select('pets').value())
198
231
 
199
232
  ### Subscribing to updates
200
233
 
201
- Atoms emit values each time they're updated. You can subscribe callbacks to them
202
- to act on updates
234
+ Atoms emit values each time they change. You can subscribe callbacks to them
235
+ to act on updates.
236
+
237
+ Subscribing fires your callback **immediately** with the atom's current value,
238
+ then again on each subsequent change. "Change" is defined by the atom's equality
239
+ comparator, which **defaults to `Object.is`** — so setting an `Object.is`-equal
240
+ value is deduped and will not re-notify (see
241
+ [Equality & change detection](#equality--change-detection)).
203
242
 
204
243
  ```
205
244
  const atom$ = Atom(3);
@@ -207,13 +246,79 @@ const atom$ = Atom(3);
207
246
  const subscription = atom$.subscribe(val => {
208
247
  console.log("Received value: ", val)
209
248
  })
249
+ // immediately logs "Received value: 3"
210
250
 
211
- atom$.set(4) // "Received value: 4"
251
+ atom$.next(3) // deduped: Object.is-equal, no log
252
+ atom$.next(4) // "Received value: 4"
212
253
 
213
254
  // Unsubscribe to clean up
214
255
  subscription.unsubscribe();
215
256
  ```
216
257
 
258
+ ### Equality & change detection
259
+
260
+ Every atom notifies its subscribers only when its value **changes**, and what
261
+ counts as a "change" is decided by an `equals(previous, next) => boolean`
262
+ comparator. When `equals` returns `true`, the atom still updates `value()` but
263
+ does **not** ping subscribers.
264
+
265
+ The default comparator for every atom — `Atom`, `derive`, `select`, and
266
+ `combine` — is `Object.is`:
267
+
268
+ - For primitives (`number`, `string`, `boolean`, ...) that's value equality.
269
+ - For objects and arrays it's **reference identity** — a brand-new object/array
270
+ is always treated as "changed," even if its contents are identical.
271
+
272
+ #### Reference vs content equality
273
+
274
+ Reference identity is the right default, but it breaks down when a producer
275
+ rebuilds fresh objects/arrays on every update (e.g. a game loop that rebuilds
276
+ its whole scene snapshot each tick). Every snapshot is a new reference, so
277
+ `Object.is` reports "changed" every tick and subscribers fire constantly even
278
+ when nothing meaningful changed.
279
+
280
+ Supply a **content** comparator to fix this:
281
+
282
+ ```
283
+ const tickets$ = frame$.derive(selectTickets, {
284
+ equals: (a, b) =>
285
+ a.length === b.length &&
286
+ a.every((t, i) => t.id === b[i].id && t.status === b[i].status),
287
+ });
288
+ // re-emits only when ticket content actually changes
289
+ ```
290
+
291
+ #### Where you can pass `equals`
292
+
293
+ ```
294
+ Atom(value, { equals }) // base atoms
295
+ parent.derive(fn, { equals }) // derived atoms
296
+ parent.select(key, { equals }) // selected atoms
297
+ ```
298
+
299
+ To force the old "notify on every update" behavior, pass `equals: () => false`.
300
+
301
+ #### You supply the comparator
302
+
303
+ `chem-rx` intentionally ships **no** `shallowEqual`/`deepEqual` helpers. A
304
+ generic shallow equal won't reach into nested or rebuilt structures (the tickets
305
+ example above would still see new element references), and a generic deep equal
306
+ is a correctness trap (`NaN`, `Date`, `Map`/`Set`, cycles, ...). Write the cheap
307
+ field comparison your data actually needs, or stamp a revision/version number on
308
+ the data and compare that.
309
+
310
+ #### React and beyond
311
+
312
+ Because equality lives on the atom (not in a React hook), content dedup benefits
313
+ **every** subscriber — React components, effects, and other derived atoms alike,
314
+ not just renders. React's `useSyncExternalStore` already bails out on
315
+ `Object.is`-equal snapshots; an atom-level `equals` is what gives you the
316
+ additional **content** dedup.
317
+
318
+ > Advanced tip: a memoizing selector that returns the _previous_ reference when
319
+ > the content is equal restores referential stability, so all downstream
320
+ > consumers can dedup off plain `Object.is` after a single comparison.
321
+
217
322
  ### Signals
218
323
 
219
324
  Sometimes, all you want is something to ping you when there's an update. Signals
@@ -284,25 +389,44 @@ function Counter() {
284
389
 
285
390
  Remember that you can mix and match for any of your needs
286
391
 
287
- ### useSelectAtom
392
+ ### useSelector
288
393
 
289
- With `useSelectAtom` you can select a specific key from an atom, and still have it live
290
- update in your react component.
394
+ With `useSelector` you can derive any value from an atom with a selector function, and
395
+ have your component live update re-rendering only when the selected value changes.
291
396
 
292
397
  ```
293
398
  import { Atom } from 'chem-rx'
294
- import { useSelectAtom } from 'chem-rx/react'
399
+ import { useSelector } from 'chem-rx/react'
295
400
 
296
401
  const count$ = Atom({ inner: 0 })
297
402
 
298
403
  function Counter() {
299
- const count = useSelectAtom(count$, 'inner')
404
+ const count = useSelector(count$, (s) => s.inner)
300
405
  return (
301
406
  <h1>
302
407
  {count}
303
408
  <button onClick={() => count$.set('inner', count + 2)}>one up</button> ...
304
409
  ```
305
410
 
411
+ `useSelector` takes an optional `equals` comparator (defaulting to `Object.is`) so you
412
+ can dedupe re-renders by content. This is the React-side counterpart to the atom-level
413
+ `equals` option described in [Equality & change detection](#equality--change-detection):
414
+ even if the parent atom rebuilds its value (and references) on every update, the
415
+ component only re-renders when the selected slice is *not* equal.
416
+
417
+ ```
418
+ const frame$ = Atom(initialFrame)
419
+
420
+ const itemsEqual = (a, b) =>
421
+ a.length === b.length && a.every((v, i) => v === b[i])
422
+
423
+ function ItemList() {
424
+ // re-renders only when `items` changes by content, not every frame
425
+ const items = useSelector(frame$, (f) => f.items, itemsEqual)
426
+ return <ul>{items.map((it) => <li key={it.id}>{it.label}</li>)}</ul>
427
+ }
428
+ ```
429
+
306
430
  ### hydrateAtoms
307
431
 
308
432
  With SSR, your atoms will likely need to be properly hydrated to prevent
package/dist/Atom.d.ts CHANGED
@@ -5,10 +5,21 @@ export type AtomTuple<T> = {
5
5
  type SelectedValue<T, K extends keyof T> = T extends readonly (infer W)[] ? W : T[K];
6
6
  type SelectKey<T> = keyof NonNullable<T>;
7
7
  type SelectValue<T, K extends SelectKey<T>> = Extract<T, null | undefined> extends never ? SelectedValue<NonNullable<T>, K> : SelectedValue<NonNullable<T>, K> | undefined;
8
+ /**
9
+ * Comparator used to decide whether an atom's value has changed.
10
+ *
11
+ * An atom notifies subscribers only when `equals(previous, next)` returns
12
+ * `false`. The default for every atom is {@link Object.is} (value equality for
13
+ * primitives, reference identity for objects/arrays). Supply a content
14
+ * comparator to dedup by data, or `() => false` to force every update to emit.
15
+ */
16
+ export type Equals<T> = (previousValue: T, nextValue: T) => boolean;
17
+ type AtomOptions<T> = {
18
+ equals?: Equals<T>;
19
+ };
8
20
  type AtomDependency<T> = {
9
21
  getSnapshot: (force?: boolean) => T;
10
22
  subscribe: (onDependencyChange: () => void) => AtomSubscription | (() => void) | void;
11
- shouldNotify?: (previousValue: T, nextValue: T) => boolean;
12
23
  };
13
24
  export declare class ReadOnlyAtom<T> {
14
25
  private _store;
@@ -16,13 +27,34 @@ export declare class ReadOnlyAtom<T> {
16
27
  private _dependency;
17
28
  private _dependencySubscription;
18
29
  private _subscriberCount;
19
- constructor(_value: T | AtomSource<T>, dependency?: AtomDependency<T>);
20
- derive<A>(deriveFn: (value: T, index: number) => A): ReadOnlyAtom<A>;
30
+ private _equals;
31
+ /**
32
+ * @param options.equals Comparator deciding when the value has changed.
33
+ * Defaults to {@link Object.is}. Subscribers are notified only when it
34
+ * returns `false`.
35
+ */
36
+ constructor(_value: T | AtomSource<T>, dependency?: AtomDependency<T>, options?: AtomOptions<T>);
37
+ /**
38
+ * Create a read-only atom whose value is derived from this atom.
39
+ *
40
+ * By default the derived atom notifies subscribers only when its computed
41
+ * output differs by {@link Object.is}. Pass `options.equals` to dedup by
42
+ * content (useful when `deriveFn` returns a fresh object/array each time), or
43
+ * `equals: () => false` to re-emit on every parent update.
44
+ */
45
+ derive<A>(deriveFn: (value: T, index: number) => A, options?: AtomOptions<A>): ReadOnlyAtom<A>;
21
46
  subscribe(observer: AtomObserver<T>): AtomSubscription;
22
47
  value(): T;
23
48
  dispose(): void;
24
49
  get<K extends SelectKey<T>>(key: K): SelectValue<T, K>;
25
- select<K extends SelectKey<T>>(key: K): ReadOnlyAtom<SelectValue<T, K>>;
50
+ /**
51
+ * Create a read-only atom that tracks `key` on this atom's value.
52
+ *
53
+ * By default the selected atom notifies subscribers only when the selected
54
+ * value differs by {@link Object.is}. Pass `options.equals` to dedup by
55
+ * content instead.
56
+ */
57
+ select<K extends SelectKey<T>>(key: K, options?: AtomOptions<SelectValue<T, K>>): ReadOnlyAtom<SelectValue<T, K>>;
26
58
  private _refresh;
27
59
  private _retainDependency;
28
60
  private _releaseDependency;
@@ -40,27 +72,38 @@ export declare class BaseAtom<T> extends ReadOnlyAtom<T> {
40
72
  set<K extends SelectKey<T>>(nextKey: K, nextValue: NonNullable<T>[K]): void;
41
73
  }
42
74
  export declare class NullableBaseAtom<T> extends BaseAtom<T | null | undefined> {
43
- constructor(_value?: T | null | undefined | AtomSource<T | null | undefined>);
75
+ constructor(_value?: T | null | undefined | AtomSource<T | null | undefined>, options?: AtomOptions<T | null | undefined>);
44
76
  next(nextVal: T | null | undefined): void;
45
77
  reset(): void;
46
78
  }
47
79
  export declare class ArrayAtom<T> extends BaseAtom<T[]> {
48
- constructor(initialValue?: T[] | AtomSource<T[]>);
80
+ constructor(initialValue?: T[] | AtomSource<T[]>, options?: AtomOptions<T[]>);
49
81
  push(nextVal: T): void;
50
82
  }
83
+ /**
84
+ * Options for the {@link Atom} factory. `readOnly` produces a
85
+ * {@link ReadOnlyAtom}; `equals` sets the change comparator (defaults to
86
+ * {@link Object.is}).
87
+ */
88
+ export type AtomFactoryOptions<T> = AtomOptions<T> & {
89
+ readOnly?: boolean;
90
+ };
51
91
  export declare function Atom<T>(value: T | AtomSource<T>, readOnly: true): ReadOnlyAtom<T>;
52
- export declare function Atom<T extends any[]>(value: AtomSource<T>): ArrayAtom<T[number]>;
53
- export declare function Atom<T>(value: AtomSource<T>): BaseAtom<T>;
54
- export declare function Atom<T extends any[]>(value?: T): ArrayAtom<T[number]>;
92
+ export declare function Atom<T>(value: T | AtomSource<T>, options: AtomFactoryOptions<T> & {
93
+ readOnly: true;
94
+ }): ReadOnlyAtom<T>;
95
+ export declare function Atom<T extends any[]>(value: AtomSource<T>, options?: AtomFactoryOptions<T>): ArrayAtom<T[number]>;
96
+ export declare function Atom<T>(value: AtomSource<T>, options?: AtomFactoryOptions<T>): BaseAtom<T>;
97
+ export declare function Atom<T extends any[]>(value: T, options?: AtomFactoryOptions<T>): ArrayAtom<T[number]>;
55
98
  export declare function Atom<T extends {
56
99
  [key: string]: T[keyof T];
57
- }>(value: T): BaseAtom<T>;
100
+ }>(value: T, options?: AtomFactoryOptions<T>): BaseAtom<T>;
58
101
  export declare function Atom<T extends {
59
102
  [key: string]: T[keyof T];
60
- }>(value?: T): NullableBaseAtom<T>;
61
- export declare function Atom<T>(value: T): BaseAtom<T>;
62
- export declare function Atom<T>(value?: T): NullableBaseAtom<T>;
63
- export declare function Atom<T>(value: T | AtomSource<T>, readOnly?: boolean): ReadOnlyAtom<T> | BaseAtom<T>;
103
+ }>(value?: T, options?: AtomFactoryOptions<T>): NullableBaseAtom<T>;
104
+ export declare function Atom<T>(value: T, options?: AtomFactoryOptions<T>): BaseAtom<T>;
105
+ export declare function Atom<T>(value?: T, options?: AtomFactoryOptions<T>): NullableBaseAtom<T>;
106
+ export declare function Atom<T>(value: T | AtomSource<T>, optionsOrReadOnly?: boolean | AtomFactoryOptions<T>): ReadOnlyAtom<T> | BaseAtom<T>;
64
107
  export declare namespace Atom {
65
108
  var combine: <A extends readonly unknown[]>(...atoms: AtomTuple<A>) => ReadOnlyAtom<A>;
66
109
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Atom.d.ts","sourceRoot":"","sources":["../src/Atom.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Atom.d.ts","sourceRoot":"","sources":["../src/Atom.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs.js CHANGED
@@ -149,13 +149,29 @@ var ValueStore = /*#__PURE__*/function () {
149
149
  return ValueStore;
150
150
  }();
151
151
 
152
+ /**
153
+ * Comparator used to decide whether an atom's value has changed.
154
+ *
155
+ * An atom notifies subscribers only when `equals(previous, next)` returns
156
+ * `false`. The default for every atom is {@link Object.is} (value equality for
157
+ * primitives, reference identity for objects/arrays). Supply a content
158
+ * comparator to dedup by data, or `() => false` to force every update to emit.
159
+ */
160
+
152
161
  var ReadOnlyAtom = /*#__PURE__*/function () {
153
- function ReadOnlyAtom(_value, dependency) {
154
- var _this = this;
162
+ /**
163
+ * @param options.equals Comparator deciding when the value has changed.
164
+ * Defaults to {@link Object.is}. Subscribers are notified only when it
165
+ * returns `false`.
166
+ */
167
+ function ReadOnlyAtom(_value, dependency, options) {
168
+ var _options$equals,
169
+ _this = this;
155
170
  this._subscriptions = [];
156
171
  this._dependency = null;
157
172
  this._dependencySubscription = null;
158
173
  this._subscriberCount = 0;
174
+ this._equals = (_options$equals = options == null ? void 0 : options.equals) != null ? _options$equals : Object.is;
159
175
  if (dependency) {
160
176
  this._dependency = dependency;
161
177
  this._store = new ValueStore(_value);
@@ -168,8 +184,17 @@ var ReadOnlyAtom = /*#__PURE__*/function () {
168
184
  this._store = new ValueStore(_value);
169
185
  }
170
186
  }
187
+
188
+ /**
189
+ * Create a read-only atom whose value is derived from this atom.
190
+ *
191
+ * By default the derived atom notifies subscribers only when its computed
192
+ * output differs by {@link Object.is}. Pass `options.equals` to dedup by
193
+ * content (useful when `deriveFn` returns a fresh object/array each time), or
194
+ * `equals: () => false` to re-emit on every parent update.
195
+ */
171
196
  var _proto = ReadOnlyAtom.prototype;
172
- _proto.derive = function derive(deriveFn) {
197
+ _proto.derive = function derive(deriveFn, options) {
173
198
  var _this2 = this;
174
199
  var index = 0;
175
200
  var hasCachedInput = false;
@@ -193,11 +218,8 @@ var ReadOnlyAtom = /*#__PURE__*/function () {
193
218
  return _this2._subscribe(onDependencyChange, {
194
219
  emitImmediately: false
195
220
  });
196
- },
197
- shouldNotify: function shouldNotify() {
198
- return true;
199
221
  }
200
- });
222
+ }, options);
201
223
  };
202
224
  _proto.subscribe = function subscribe(observer) {
203
225
  return this._subscribe(observer);
@@ -221,8 +243,16 @@ var ReadOnlyAtom = /*#__PURE__*/function () {
221
243
  _proto.get = function get(key) {
222
244
  var val = this.value();
223
245
  return val == null ? void 0 : val[key];
224
- };
225
- _proto.select = function select(key) {
246
+ }
247
+
248
+ /**
249
+ * Create a read-only atom that tracks `key` on this atom's value.
250
+ *
251
+ * By default the selected atom notifies subscribers only when the selected
252
+ * value differs by {@link Object.is}. Pass `options.equals` to dedup by
253
+ * content instead.
254
+ */;
255
+ _proto.select = function select(key, options) {
226
256
  var _this3 = this;
227
257
  var getSnapshot = function getSnapshot() {
228
258
  var value = _this3.value();
@@ -235,10 +265,9 @@ var ReadOnlyAtom = /*#__PURE__*/function () {
235
265
  emitImmediately: false
236
266
  });
237
267
  }
238
- });
268
+ }, options);
239
269
  };
240
270
  _proto._refresh = function _refresh(emit, force) {
241
- var _this$_dependency$sho, _this$_dependency$sho2, _this$_dependency;
242
271
  if (force === void 0) {
243
272
  force = false;
244
273
  }
@@ -247,7 +276,7 @@ var ReadOnlyAtom = /*#__PURE__*/function () {
247
276
  }
248
277
  var previousValue = this._store.getValue();
249
278
  var nextValue = this._dependency.getSnapshot(force);
250
- var shouldNotify = emit && ((_this$_dependency$sho = (_this$_dependency$sho2 = (_this$_dependency = this._dependency).shouldNotify) == null ? void 0 : _this$_dependency$sho2.call(_this$_dependency, previousValue, nextValue)) != null ? _this$_dependency$sho : !Object.is(previousValue, nextValue));
279
+ var shouldNotify = emit && !this._equals(previousValue, nextValue);
251
280
  if (shouldNotify) {
252
281
  this._store.next(nextValue);
253
282
  } else {
@@ -281,7 +310,12 @@ var ReadOnlyAtom = /*#__PURE__*/function () {
281
310
 
282
311
  /** @internal */;
283
312
  _proto._next = function _next(value) {
284
- this._store.next(value);
313
+ var previousValue = this._store.getValue();
314
+ if (this._equals(previousValue, value)) {
315
+ this._store.setValue(value);
316
+ } else {
317
+ this._store.next(value);
318
+ }
285
319
  }
286
320
 
287
321
  /** @internal */;
@@ -333,8 +367,8 @@ var BaseAtom = /*#__PURE__*/function (_ReadOnlyAtom2) {
333
367
  return BaseAtom;
334
368
  }(ReadOnlyAtom);
335
369
  var NullableBaseAtom = /*#__PURE__*/function (_BaseAtom) {
336
- function NullableBaseAtom(_value) {
337
- return _BaseAtom.call(this, _value) || this;
370
+ function NullableBaseAtom(_value, options) {
371
+ return _BaseAtom.call(this, _value, undefined, options) || this;
338
372
  }
339
373
  _inheritsLoose(NullableBaseAtom, _BaseAtom);
340
374
  var _proto3 = NullableBaseAtom.prototype;
@@ -347,12 +381,12 @@ var NullableBaseAtom = /*#__PURE__*/function (_BaseAtom) {
347
381
  return NullableBaseAtom;
348
382
  }(BaseAtom);
349
383
  var ArrayAtom = /*#__PURE__*/function (_BaseAtom2) {
350
- function ArrayAtom(initialValue) {
384
+ function ArrayAtom(initialValue, options) {
351
385
  var _this6;
352
386
  if (initialValue === undefined) {
353
- _this6 = _BaseAtom2.call(this, []) || this;
387
+ _this6 = _BaseAtom2.call(this, [], undefined, options) || this;
354
388
  } else {
355
- _this6 = _BaseAtom2.call(this, initialValue) || this;
389
+ _this6 = _BaseAtom2.call(this, initialValue, undefined, options) || this;
356
390
  }
357
391
  return _assertThisInitialized(_this6);
358
392
  }
@@ -363,27 +397,44 @@ var ArrayAtom = /*#__PURE__*/function (_BaseAtom2) {
363
397
  };
364
398
  return ArrayAtom;
365
399
  }(BaseAtom);
366
- function Atom(_value, readOnly) {
367
- if (readOnly === void 0) {
368
- readOnly = false;
369
- }
400
+
401
+ /**
402
+ * Options for the {@link Atom} factory. `readOnly` produces a
403
+ * {@link ReadOnlyAtom}; `equals` sets the change comparator (defaults to
404
+ * {@link Object.is}).
405
+ */
406
+
407
+ // Legacy positional `readOnly` form.
408
+
409
+ // Options-object form.
410
+
411
+ function Atom(_value, optionsOrReadOnly) {
412
+ var factoryOptions = typeof optionsOrReadOnly === "boolean" ? {
413
+ readOnly: optionsOrReadOnly
414
+ } : optionsOrReadOnly != null ? optionsOrReadOnly : {};
415
+ var _factoryOptions$readO = factoryOptions.readOnly,
416
+ readOnly = _factoryOptions$readO === void 0 ? false : _factoryOptions$readO,
417
+ equals = factoryOptions.equals;
418
+ var atomOptions = equals ? {
419
+ equals: equals
420
+ } : undefined;
370
421
  if (readOnly) {
371
- return new ReadOnlyAtom(_value);
422
+ return new ReadOnlyAtom(_value, undefined, atomOptions);
372
423
  }
373
424
  if (isAtomSource(_value)) {
374
425
  var sourceValue = readAtomSourceValue(_value);
375
426
  if (Array.isArray(sourceValue)) {
376
- return new ArrayAtom(_value);
427
+ return new ArrayAtom(_value, atomOptions);
377
428
  }
378
- return new BaseAtom(_value);
429
+ return new BaseAtom(_value, undefined, atomOptions);
379
430
  }
380
431
  if (Array.isArray(_value)) {
381
- return new ArrayAtom(_value);
432
+ return new ArrayAtom(_value, atomOptions);
382
433
  }
383
434
  if (_value == null) {
384
- return new NullableBaseAtom();
435
+ return new NullableBaseAtom(undefined, atomOptions);
385
436
  }
386
- return new BaseAtom(_value);
437
+ return new BaseAtom(_value, undefined, atomOptions);
387
438
  }
388
439
  Atom.combine = function () {
389
440
  for (var _len = arguments.length, atoms = new Array(_len), _key = 0; _key < _len; _key++) {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { Atom } from "./Atom";
2
+ export type { Equals, AtomFactoryOptions } from "./Atom";
2
3
  export { hydrateAtoms } from "./useHydrateAtoms";
3
4
  export { Signal } from "./Signal";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC"}
@@ -150,13 +150,29 @@ var chemicalRx = (function (exports) {
150
150
  return ValueStore;
151
151
  }();
152
152
 
153
+ /**
154
+ * Comparator used to decide whether an atom's value has changed.
155
+ *
156
+ * An atom notifies subscribers only when `equals(previous, next)` returns
157
+ * `false`. The default for every atom is {@link Object.is} (value equality for
158
+ * primitives, reference identity for objects/arrays). Supply a content
159
+ * comparator to dedup by data, or `() => false` to force every update to emit.
160
+ */
161
+
153
162
  var ReadOnlyAtom = /*#__PURE__*/function () {
154
- function ReadOnlyAtom(_value, dependency) {
155
- var _this = this;
163
+ /**
164
+ * @param options.equals Comparator deciding when the value has changed.
165
+ * Defaults to {@link Object.is}. Subscribers are notified only when it
166
+ * returns `false`.
167
+ */
168
+ function ReadOnlyAtom(_value, dependency, options) {
169
+ var _options$equals,
170
+ _this = this;
156
171
  this._subscriptions = [];
157
172
  this._dependency = null;
158
173
  this._dependencySubscription = null;
159
174
  this._subscriberCount = 0;
175
+ this._equals = (_options$equals = options == null ? void 0 : options.equals) != null ? _options$equals : Object.is;
160
176
  if (dependency) {
161
177
  this._dependency = dependency;
162
178
  this._store = new ValueStore(_value);
@@ -169,8 +185,17 @@ var chemicalRx = (function (exports) {
169
185
  this._store = new ValueStore(_value);
170
186
  }
171
187
  }
188
+
189
+ /**
190
+ * Create a read-only atom whose value is derived from this atom.
191
+ *
192
+ * By default the derived atom notifies subscribers only when its computed
193
+ * output differs by {@link Object.is}. Pass `options.equals` to dedup by
194
+ * content (useful when `deriveFn` returns a fresh object/array each time), or
195
+ * `equals: () => false` to re-emit on every parent update.
196
+ */
172
197
  var _proto = ReadOnlyAtom.prototype;
173
- _proto.derive = function derive(deriveFn) {
198
+ _proto.derive = function derive(deriveFn, options) {
174
199
  var _this2 = this;
175
200
  var index = 0;
176
201
  var hasCachedInput = false;
@@ -194,11 +219,8 @@ var chemicalRx = (function (exports) {
194
219
  return _this2._subscribe(onDependencyChange, {
195
220
  emitImmediately: false
196
221
  });
197
- },
198
- shouldNotify: function shouldNotify() {
199
- return true;
200
222
  }
201
- });
223
+ }, options);
202
224
  };
203
225
  _proto.subscribe = function subscribe(observer) {
204
226
  return this._subscribe(observer);
@@ -222,8 +244,16 @@ var chemicalRx = (function (exports) {
222
244
  _proto.get = function get(key) {
223
245
  var val = this.value();
224
246
  return val == null ? void 0 : val[key];
225
- };
226
- _proto.select = function select(key) {
247
+ }
248
+
249
+ /**
250
+ * Create a read-only atom that tracks `key` on this atom's value.
251
+ *
252
+ * By default the selected atom notifies subscribers only when the selected
253
+ * value differs by {@link Object.is}. Pass `options.equals` to dedup by
254
+ * content instead.
255
+ */;
256
+ _proto.select = function select(key, options) {
227
257
  var _this3 = this;
228
258
  var getSnapshot = function getSnapshot() {
229
259
  var value = _this3.value();
@@ -236,10 +266,9 @@ var chemicalRx = (function (exports) {
236
266
  emitImmediately: false
237
267
  });
238
268
  }
239
- });
269
+ }, options);
240
270
  };
241
271
  _proto._refresh = function _refresh(emit, force) {
242
- var _this$_dependency$sho, _this$_dependency$sho2, _this$_dependency;
243
272
  if (force === void 0) {
244
273
  force = false;
245
274
  }
@@ -248,7 +277,7 @@ var chemicalRx = (function (exports) {
248
277
  }
249
278
  var previousValue = this._store.getValue();
250
279
  var nextValue = this._dependency.getSnapshot(force);
251
- var shouldNotify = emit && ((_this$_dependency$sho = (_this$_dependency$sho2 = (_this$_dependency = this._dependency).shouldNotify) == null ? void 0 : _this$_dependency$sho2.call(_this$_dependency, previousValue, nextValue)) != null ? _this$_dependency$sho : !Object.is(previousValue, nextValue));
280
+ var shouldNotify = emit && !this._equals(previousValue, nextValue);
252
281
  if (shouldNotify) {
253
282
  this._store.next(nextValue);
254
283
  } else {
@@ -282,7 +311,12 @@ var chemicalRx = (function (exports) {
282
311
 
283
312
  /** @internal */;
284
313
  _proto._next = function _next(value) {
285
- this._store.next(value);
314
+ var previousValue = this._store.getValue();
315
+ if (this._equals(previousValue, value)) {
316
+ this._store.setValue(value);
317
+ } else {
318
+ this._store.next(value);
319
+ }
286
320
  }
287
321
 
288
322
  /** @internal */;
@@ -334,8 +368,8 @@ var chemicalRx = (function (exports) {
334
368
  return BaseAtom;
335
369
  }(ReadOnlyAtom);
336
370
  var NullableBaseAtom = /*#__PURE__*/function (_BaseAtom) {
337
- function NullableBaseAtom(_value) {
338
- return _BaseAtom.call(this, _value) || this;
371
+ function NullableBaseAtom(_value, options) {
372
+ return _BaseAtom.call(this, _value, undefined, options) || this;
339
373
  }
340
374
  _inheritsLoose(NullableBaseAtom, _BaseAtom);
341
375
  var _proto3 = NullableBaseAtom.prototype;
@@ -348,12 +382,12 @@ var chemicalRx = (function (exports) {
348
382
  return NullableBaseAtom;
349
383
  }(BaseAtom);
350
384
  var ArrayAtom = /*#__PURE__*/function (_BaseAtom2) {
351
- function ArrayAtom(initialValue) {
385
+ function ArrayAtom(initialValue, options) {
352
386
  var _this6;
353
387
  if (initialValue === undefined) {
354
- _this6 = _BaseAtom2.call(this, []) || this;
388
+ _this6 = _BaseAtom2.call(this, [], undefined, options) || this;
355
389
  } else {
356
- _this6 = _BaseAtom2.call(this, initialValue) || this;
390
+ _this6 = _BaseAtom2.call(this, initialValue, undefined, options) || this;
357
391
  }
358
392
  return _assertThisInitialized(_this6);
359
393
  }
@@ -364,27 +398,44 @@ var chemicalRx = (function (exports) {
364
398
  };
365
399
  return ArrayAtom;
366
400
  }(BaseAtom);
367
- function Atom(_value, readOnly) {
368
- if (readOnly === void 0) {
369
- readOnly = false;
370
- }
401
+
402
+ /**
403
+ * Options for the {@link Atom} factory. `readOnly` produces a
404
+ * {@link ReadOnlyAtom}; `equals` sets the change comparator (defaults to
405
+ * {@link Object.is}).
406
+ */
407
+
408
+ // Legacy positional `readOnly` form.
409
+
410
+ // Options-object form.
411
+
412
+ function Atom(_value, optionsOrReadOnly) {
413
+ var factoryOptions = typeof optionsOrReadOnly === "boolean" ? {
414
+ readOnly: optionsOrReadOnly
415
+ } : optionsOrReadOnly != null ? optionsOrReadOnly : {};
416
+ var _factoryOptions$readO = factoryOptions.readOnly,
417
+ readOnly = _factoryOptions$readO === void 0 ? false : _factoryOptions$readO,
418
+ equals = factoryOptions.equals;
419
+ var atomOptions = equals ? {
420
+ equals: equals
421
+ } : undefined;
371
422
  if (readOnly) {
372
- return new ReadOnlyAtom(_value);
423
+ return new ReadOnlyAtom(_value, undefined, atomOptions);
373
424
  }
374
425
  if (isAtomSource(_value)) {
375
426
  var sourceValue = readAtomSourceValue(_value);
376
427
  if (Array.isArray(sourceValue)) {
377
- return new ArrayAtom(_value);
428
+ return new ArrayAtom(_value, atomOptions);
378
429
  }
379
- return new BaseAtom(_value);
430
+ return new BaseAtom(_value, undefined, atomOptions);
380
431
  }
381
432
  if (Array.isArray(_value)) {
382
- return new ArrayAtom(_value);
433
+ return new ArrayAtom(_value, atomOptions);
383
434
  }
384
435
  if (_value == null) {
385
- return new NullableBaseAtom();
436
+ return new NullableBaseAtom(undefined, atomOptions);
386
437
  }
387
- return new BaseAtom(_value);
438
+ return new BaseAtom(_value, undefined, atomOptions);
388
439
  }
389
440
  Atom.combine = function () {
390
441
  for (var _len = arguments.length, atoms = new Array(_len), _key = 0; _key < _len; _key++) {
package/dist/index.mjs CHANGED
@@ -95,12 +95,28 @@ class ValueStore {
95
95
  }
96
96
  }
97
97
 
98
+ /**
99
+ * Comparator used to decide whether an atom's value has changed.
100
+ *
101
+ * An atom notifies subscribers only when `equals(previous, next)` returns
102
+ * `false`. The default for every atom is {@link Object.is} (value equality for
103
+ * primitives, reference identity for objects/arrays). Supply a content
104
+ * comparator to dedup by data, or `() => false` to force every update to emit.
105
+ */
106
+
98
107
  class ReadOnlyAtom {
99
- constructor(_value, dependency) {
108
+ /**
109
+ * @param options.equals Comparator deciding when the value has changed.
110
+ * Defaults to {@link Object.is}. Subscribers are notified only when it
111
+ * returns `false`.
112
+ */
113
+ constructor(_value, dependency, options) {
114
+ var _options$equals;
100
115
  this._subscriptions = [];
101
116
  this._dependency = null;
102
117
  this._dependencySubscription = null;
103
118
  this._subscriberCount = 0;
119
+ this._equals = (_options$equals = options == null ? void 0 : options.equals) != null ? _options$equals : Object.is;
104
120
  if (dependency) {
105
121
  this._dependency = dependency;
106
122
  this._store = new ValueStore(_value);
@@ -113,7 +129,16 @@ class ReadOnlyAtom {
113
129
  this._store = new ValueStore(_value);
114
130
  }
115
131
  }
116
- derive(deriveFn) {
132
+
133
+ /**
134
+ * Create a read-only atom whose value is derived from this atom.
135
+ *
136
+ * By default the derived atom notifies subscribers only when its computed
137
+ * output differs by {@link Object.is}. Pass `options.equals` to dedup by
138
+ * content (useful when `deriveFn` returns a fresh object/array each time), or
139
+ * `equals: () => false` to re-emit on every parent update.
140
+ */
141
+ derive(deriveFn, options) {
117
142
  let index = 0;
118
143
  let hasCachedInput = false;
119
144
  let cachedInput;
@@ -131,9 +156,8 @@ class ReadOnlyAtom {
131
156
  getSnapshot,
132
157
  subscribe: onDependencyChange => this._subscribe(onDependencyChange, {
133
158
  emitImmediately: false
134
- }),
135
- shouldNotify: () => true
136
- });
159
+ })
160
+ }, options);
137
161
  }
138
162
  subscribe(observer) {
139
163
  return this._subscribe(observer);
@@ -157,7 +181,15 @@ class ReadOnlyAtom {
157
181
  const val = this.value();
158
182
  return val == null ? void 0 : val[key];
159
183
  }
160
- select(key) {
184
+
185
+ /**
186
+ * Create a read-only atom that tracks `key` on this atom's value.
187
+ *
188
+ * By default the selected atom notifies subscribers only when the selected
189
+ * value differs by {@link Object.is}. Pass `options.equals` to dedup by
190
+ * content instead.
191
+ */
192
+ select(key, options) {
161
193
  const getSnapshot = () => {
162
194
  const value = this.value();
163
195
  return value == null ? void 0 : value[key];
@@ -167,16 +199,15 @@ class ReadOnlyAtom {
167
199
  subscribe: onDependencyChange => this._subscribe(onDependencyChange, {
168
200
  emitImmediately: false
169
201
  })
170
- });
202
+ }, options);
171
203
  }
172
204
  _refresh(emit, force = false) {
173
- var _this$_dependency$sho, _this$_dependency$sho2, _this$_dependency;
174
205
  if (!this._dependency) {
175
206
  return this._store.getValue();
176
207
  }
177
208
  const previousValue = this._store.getValue();
178
209
  const nextValue = this._dependency.getSnapshot(force);
179
- const shouldNotify = emit && ((_this$_dependency$sho = (_this$_dependency$sho2 = (_this$_dependency = this._dependency).shouldNotify) == null ? void 0 : _this$_dependency$sho2.call(_this$_dependency, previousValue, nextValue)) != null ? _this$_dependency$sho : !Object.is(previousValue, nextValue));
210
+ const shouldNotify = emit && !this._equals(previousValue, nextValue);
180
211
  if (shouldNotify) {
181
212
  this._store.next(nextValue);
182
213
  } else {
@@ -209,7 +240,12 @@ class ReadOnlyAtom {
209
240
 
210
241
  /** @internal */
211
242
  _next(value) {
212
- this._store.next(value);
243
+ const previousValue = this._store.getValue();
244
+ if (this._equals(previousValue, value)) {
245
+ this._store.setValue(value);
246
+ } else {
247
+ this._store.next(value);
248
+ }
213
249
  }
214
250
 
215
251
  /** @internal */
@@ -254,8 +290,8 @@ class BaseAtom extends ReadOnlyAtom {
254
290
  }
255
291
  }
256
292
  class NullableBaseAtom extends BaseAtom {
257
- constructor(_value) {
258
- super(_value);
293
+ constructor(_value, options) {
294
+ super(_value, undefined, options);
259
295
  }
260
296
  next(nextVal) {
261
297
  this._next(nextVal);
@@ -265,35 +301,56 @@ class NullableBaseAtom extends BaseAtom {
265
301
  }
266
302
  }
267
303
  class ArrayAtom extends BaseAtom {
268
- constructor(initialValue) {
304
+ constructor(initialValue, options) {
269
305
  if (initialValue === undefined) {
270
- super([]);
306
+ super([], undefined, options);
271
307
  } else {
272
- super(initialValue);
308
+ super(initialValue, undefined, options);
273
309
  }
274
310
  }
275
311
  push(nextVal) {
276
312
  this._next([...this.value(), nextVal]);
277
313
  }
278
314
  }
279
- function Atom(_value, readOnly = false) {
315
+
316
+ /**
317
+ * Options for the {@link Atom} factory. `readOnly` produces a
318
+ * {@link ReadOnlyAtom}; `equals` sets the change comparator (defaults to
319
+ * {@link Object.is}).
320
+ */
321
+
322
+ // Legacy positional `readOnly` form.
323
+
324
+ // Options-object form.
325
+
326
+ function Atom(_value, optionsOrReadOnly) {
327
+ const factoryOptions = typeof optionsOrReadOnly === "boolean" ? {
328
+ readOnly: optionsOrReadOnly
329
+ } : optionsOrReadOnly != null ? optionsOrReadOnly : {};
330
+ const {
331
+ readOnly = false,
332
+ equals
333
+ } = factoryOptions;
334
+ const atomOptions = equals ? {
335
+ equals
336
+ } : undefined;
280
337
  if (readOnly) {
281
- return new ReadOnlyAtom(_value);
338
+ return new ReadOnlyAtom(_value, undefined, atomOptions);
282
339
  }
283
340
  if (isAtomSource(_value)) {
284
341
  const sourceValue = readAtomSourceValue(_value);
285
342
  if (Array.isArray(sourceValue)) {
286
- return new ArrayAtom(_value);
343
+ return new ArrayAtom(_value, atomOptions);
287
344
  }
288
- return new BaseAtom(_value);
345
+ return new BaseAtom(_value, undefined, atomOptions);
289
346
  }
290
347
  if (Array.isArray(_value)) {
291
- return new ArrayAtom(_value);
348
+ return new ArrayAtom(_value, atomOptions);
292
349
  }
293
350
  if (_value == null) {
294
- return new NullableBaseAtom();
351
+ return new NullableBaseAtom(undefined, atomOptions);
295
352
  }
296
- return new BaseAtom(_value);
353
+ return new BaseAtom(_value, undefined, atomOptions);
297
354
  }
298
355
  Atom.combine = (...atoms) => {
299
356
  const getSnapshot = () => atoms.map(atom => atom.value());
package/dist/react.cjs.js CHANGED
@@ -38,13 +38,57 @@ function useSignal(signal, callback, id) {
38
38
  }, [signal, callback, id]);
39
39
  }
40
40
 
41
- function useSelectAtom(atom, key) {
42
- var selectedAtom = react.useMemo(function () {
43
- return atom.select(key);
44
- }, [atom, key]);
45
- return useAtom(selectedAtom);
41
+ function createSelectorMemo(selector, equals) {
42
+ var hasValue = false;
43
+ var prevSnapshot;
44
+ var prevSelection;
45
+ return function (snapshot) {
46
+ if (hasValue && Object.is(prevSnapshot, snapshot)) {
47
+ return prevSelection;
48
+ }
49
+ var next = selector(snapshot);
50
+ if (hasValue && equals(prevSelection, next)) {
51
+ prevSnapshot = snapshot;
52
+ return prevSelection;
53
+ }
54
+ hasValue = true;
55
+ prevSnapshot = snapshot;
56
+ prevSelection = next;
57
+ return next;
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Subscribe to a slice of an atom, re-rendering only when the selected value
63
+ * changes. The optional `equals` comparator dedupes by content (defaults to
64
+ * `Object.is`), so a parent atom that churns references every update will not
65
+ * re-render the component when the selected slice is content-equal.
66
+ */
67
+ function useSelector(atom, selector, equals) {
68
+ if (equals === void 0) {
69
+ equals = Object.is;
70
+ }
71
+ var select = react.useMemo(function () {
72
+ return createSelectorMemo(selector, equals);
73
+ }, [selector, equals]);
74
+ return react.useSyncExternalStore(function (onStoreChange) {
75
+ var subscribed = false;
76
+ var subscription = atom.subscribe(function () {
77
+ if (subscribed) {
78
+ onStoreChange();
79
+ }
80
+ });
81
+ subscribed = true;
82
+ return function () {
83
+ subscription.unsubscribe();
84
+ };
85
+ }, function () {
86
+ return select(atom.value());
87
+ }, function () {
88
+ return select(atom.value());
89
+ });
46
90
  }
47
91
 
48
92
  exports.useAtom = useAtom;
49
- exports.useSelectAtom = useSelectAtom;
93
+ exports.useSelector = useSelector;
50
94
  exports.useSignal = useSignal;
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { useAtom } from "./useAtom";
2
2
  export { useSignal } from "./useSignal";
3
- export { useSelectAtom } from "./useSelectAtom";
3
+ export { useSelector } from "./useSelector";
4
4
  //# sourceMappingURL=react.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
package/dist/react.mjs CHANGED
@@ -32,9 +32,46 @@ function useSignal(signal, callback, id) {
32
32
  }, [signal, callback, id]);
33
33
  }
34
34
 
35
- function useSelectAtom(atom, key) {
36
- const selectedAtom = useMemo(() => atom.select(key), [atom, key]);
37
- return useAtom(selectedAtom);
35
+ function createSelectorMemo(selector, equals) {
36
+ let hasValue = false;
37
+ let prevSnapshot;
38
+ let prevSelection;
39
+ return snapshot => {
40
+ if (hasValue && Object.is(prevSnapshot, snapshot)) {
41
+ return prevSelection;
42
+ }
43
+ const next = selector(snapshot);
44
+ if (hasValue && equals(prevSelection, next)) {
45
+ prevSnapshot = snapshot;
46
+ return prevSelection;
47
+ }
48
+ hasValue = true;
49
+ prevSnapshot = snapshot;
50
+ prevSelection = next;
51
+ return next;
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Subscribe to a slice of an atom, re-rendering only when the selected value
57
+ * changes. The optional `equals` comparator dedupes by content (defaults to
58
+ * `Object.is`), so a parent atom that churns references every update will not
59
+ * re-render the component when the selected slice is content-equal.
60
+ */
61
+ function useSelector(atom, selector, equals = Object.is) {
62
+ const select = useMemo(() => createSelectorMemo(selector, equals), [selector, equals]);
63
+ return useSyncExternalStore(onStoreChange => {
64
+ let subscribed = false;
65
+ const subscription = atom.subscribe(() => {
66
+ if (subscribed) {
67
+ onStoreChange();
68
+ }
69
+ });
70
+ subscribed = true;
71
+ return () => {
72
+ subscription.unsubscribe();
73
+ };
74
+ }, () => select(atom.value()), () => select(atom.value()));
38
75
  }
39
76
 
40
- export { useAtom, useSelectAtom, useSignal };
77
+ export { useAtom, useSelector, useSignal };
@@ -0,0 +1,10 @@
1
+ import { Equals, ReadOnlyAtom } from "./Atom";
2
+ export declare function createSelectorMemo<T, R>(selector: (value: T) => R, equals: Equals<R>): (snapshot: T) => R;
3
+ /**
4
+ * Subscribe to a slice of an atom, re-rendering only when the selected value
5
+ * changes. The optional `equals` comparator dedupes by content (defaults to
6
+ * `Object.is`), so a parent atom that churns references every update will not
7
+ * re-render the component when the selected slice is content-equal.
8
+ */
9
+ export declare function useSelector<T, R>(atom: ReadOnlyAtom<T>, selector: (value: T) => R, equals?: Equals<R>): R;
10
+ //# sourceMappingURL=useSelector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSelector.d.ts","sourceRoot":"","sources":["../src/useSelector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,EACzB,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAChB,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAkBpB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,CAAC,EAC9B,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,EACzB,MAAM,GAAE,MAAM,CAAC,CAAC,CAAa,GAC5B,CAAC,CAqBH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chem-rx",
3
- "version": "0.1.0",
3
+ "version": "0.4.0",
4
4
  "description": "react state primitives with framework-agnostic atoms",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.mjs",
@@ -8,6 +8,7 @@
8
8
  "files": [
9
9
  "dist",
10
10
  "README.md",
11
+ "CHANGELOG.md",
11
12
  "LICENSE"
12
13
  ],
13
14
  "scripts": {
@@ -15,7 +16,7 @@
15
16
  "test": "jest",
16
17
  "clean": "rm -rf ./dist",
17
18
  "build": "pnpm run prebuild && tsc --emitDeclarationOnly && rollup -c",
18
- "bump": "pnpm run build && pnpm version patch"
19
+ "prepublishOnly": "pnpm run test && pnpm run build"
19
20
  },
20
21
  "repository": {
21
22
  "type": "git",
@@ -35,9 +36,14 @@
35
36
  "@babel/preset-typescript": "^7.22.5",
36
37
  "@rollup/plugin-babel": "^6.0.3",
37
38
  "@rollup/plugin-node-resolve": "^15.1.0",
39
+ "@testing-library/dom": "^10.4.1",
40
+ "@testing-library/react": "^16.3.2",
38
41
  "@types/jest": "^29.5.3",
39
42
  "@types/react": "^18.2.20",
40
43
  "jest": "^29.6.2",
44
+ "jest-environment-jsdom": "^29.7.0",
45
+ "react": "^18.3.1",
46
+ "react-dom": "^18.3.1",
41
47
  "rimraf": "^5.0.1",
42
48
  "rollup": "^3.28.0",
43
49
  "rollup-plugin-size-snapshot": "^0.12.0",
@@ -1,5 +0,0 @@
1
- import { BaseAtom, NullableBaseAtom, ReadOnlyAtom } from "./Atom";
2
- export declare function useSelectAtom<T extends {
3
- [key in K]: V;
4
- }, K extends keyof T, V = T[K]>(atom: NullableBaseAtom<T> | BaseAtom<T> | ReadOnlyAtom<T>, key: K): T[K];
5
- //# sourceMappingURL=useSelectAtom.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useSelectAtom.d.ts","sourceRoot":"","sources":["../src/useSelectAtom.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGlE,wBAAgB,aAAa,CAC3B,CAAC,SACG;KACG,GAAG,IAAI,CAAC,GAAG,CAAC;CACd,EACL,CAAC,SAAS,MAAM,CAAC,EACjB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EACR,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAMzE"}