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 +42 -0
- package/README.md +133 -9
- package/dist/Atom.d.ts +57 -14
- package/dist/Atom.d.ts.map +1 -1
- package/dist/index.cjs.js +79 -28
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.iife.js +79 -28
- package/dist/index.mjs +79 -22
- package/dist/react.cjs.js +50 -6
- package/dist/react.d.ts +1 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.mjs +41 -4
- package/dist/useSelector.d.ts +10 -0
- package/dist/useSelector.d.ts.map +1 -0
- package/package.json +8 -2
- package/dist/useSelectAtom.d.ts +0 -5
- package/dist/useSelectAtom.d.ts.map +0 -1
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
|
-
|
|
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
|
|
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$.
|
|
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
|
-
###
|
|
392
|
+
### useSelector
|
|
288
393
|
|
|
289
|
-
With `
|
|
290
|
-
update
|
|
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 {
|
|
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 =
|
|
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
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
|
53
|
-
|
|
54
|
-
|
|
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>,
|
|
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
|
}
|
package/dist/Atom.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Atom.d.ts","sourceRoot":"","sources":["../src/Atom.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,gBAAgB,EAMjB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;KACxB,CAAC,IAAI,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACnC,CAAC;AAEF,KAAK,aAAa,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,SAAS,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GACrE,CAAC,GACD,CAAC,CAAC,CAAC,CAAC,CAAC;AAET,KAAK,SAAS,CAAC,CAAC,IAAI,MAAM,WAAW,CAAC,CAAC,CAAC,CAAC;AAEzC,KAAK,WAAW,CAAC,CAAC,EAAE,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,IACxC,OAAO,CAAC,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC,SAAS,KAAK,GACtC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAChC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;AAEnD,KAAK,cAAc,CAAC,CAAC,IAAI;IACvB,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC,CAAC;IACpC,SAAS,EAAE,CACT,kBAAkB,EAAE,MAAM,IAAI,KAC3B,gBAAgB,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"Atom.d.ts","sourceRoot":"","sources":["../src/Atom.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,gBAAgB,EAMjB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;KACxB,CAAC,IAAI,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CACnC,CAAC;AAEF,KAAK,aAAa,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,SAAS,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GACrE,CAAC,GACD,CAAC,CAAC,CAAC,CAAC,CAAC;AAET,KAAK,SAAS,CAAC,CAAC,IAAI,MAAM,WAAW,CAAC,CAAC,CAAC,CAAC;AAEzC,KAAK,WAAW,CAAC,CAAC,EAAE,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,IACxC,OAAO,CAAC,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC,SAAS,KAAK,GACtC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAChC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;AAEnD;;;;;;;GAOG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,KAAK,OAAO,CAAC;AAEpE,KAAK,WAAW,CAAC,CAAC,IAAI;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;CACpB,CAAC;AAEF,KAAK,cAAc,CAAC,CAAC,IAAI;IACvB,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC,CAAC;IACpC,SAAS,EAAE,CACT,kBAAkB,EAAE,MAAM,IAAI,KAC3B,gBAAgB,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;CAC7C,CAAC;AAEF,qBAAa,YAAY,CAAC,CAAC;IACzB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,cAAc,CAA0B;IAChD,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,uBAAuB,CAAiC;IAChE,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,OAAO,CAAY;IAE3B;;;;OAIG;gBAED,MAAM,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EACzB,UAAU,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,EAC9B,OAAO,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC;IAmB1B;;;;;;;OAOG;IACH,MAAM,CAAC,CAAC,EACN,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,EACxC,OAAO,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,GACvB,YAAY,CAAC,CAAC,CAAC;IA6BlB,SAAS,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,gBAAgB;IAItD,KAAK;IAQL,OAAO;IAUP,GAAG,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC;IAKtD;;;;;;OAMG;IACH,MAAM,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAC3B,GAAG,EAAE,CAAC,EACN,OAAO,CAAC,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GACvC,YAAY,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAmBlC,OAAO,CAAC,QAAQ;IAkBhB,OAAO,CAAC,iBAAiB;IAgBzB,OAAO,CAAC,kBAAkB;IAa1B,gBAAgB;IAChB,KAAK,CAAC,KAAK,EAAE,CAAC;IAUd,gBAAgB;IAChB,UAAU,CACR,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,EACzB,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GACtC,gBAAgB;IAuBnB,gBAAgB;IAChB,gBAAgB,CACd,YAAY,EAAE,gBAAgB,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,GACnD,gBAAgB;CAKpB;AAED,qBAAa,QAAQ,CAAC,CAAC,CAAE,SAAQ,YAAY,CAAC,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO,EAAE,CAAC;IAIf,GAAG,CAAC,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAYrE;AAED,qBAAa,gBAAgB,CAAC,CAAC,CAAE,SAAQ,QAAQ,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;gBAEnE,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,UAAU,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,EAChE,OAAO,CAAC,EAAE,WAAW,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IAS7C,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS;IAIlC,KAAK;CAGN;AAED,qBAAa,SAAS,CAAC,CAAC,CAAE,SAAQ,QAAQ,CAAC,CAAC,EAAE,CAAC;gBACjC,YAAY,CAAC,EAAE,CAAC,EAAE,GAAG,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC;IAQ5E,IAAI,CAAC,OAAO,EAAE,CAAC;CAGhB;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAG5E,wBAAgB,IAAI,CAAC,CAAC,EACpB,KAAK,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EACxB,QAAQ,EAAE,IAAI,GACb,YAAY,CAAC,CAAC,CAAC,CAAC;AAEnB,wBAAgB,IAAI,CAAC,CAAC,EACpB,KAAK,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EACxB,OAAO,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,GAClD,YAAY,CAAC,CAAC,CAAC,CAAC;AACnB,wBAAgB,IAAI,CAAC,CAAC,SAAS,GAAG,EAAE,EAClC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EACpB,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC9B,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AACxB,wBAAgB,IAAI,CAAC,CAAC,EACpB,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EACpB,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC9B,QAAQ,CAAC,CAAC,CAAC,CAAC;AACf,wBAAgB,IAAI,CAAC,CAAC,SAAS,GAAG,EAAE,EAClC,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC9B,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AACxB,wBAAgB,IAAI,CAAC,CAAC,SAAS;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;CAAE,EAC1D,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC9B,QAAQ,CAAC,CAAC,CAAC,CAAC;AACf,wBAAgB,IAAI,CAAC,CAAC,SAAS;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;CAAE,EAC1D,KAAK,CAAC,EAAE,CAAC,EACT,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC9B,gBAAgB,CAAC,CAAC,CAAC,CAAC;AACvB,wBAAgB,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;AAChF,wBAAgB,IAAI,CAAC,CAAC,EACpB,KAAK,CAAC,EAAE,CAAC,EACT,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC9B,gBAAgB,CAAC,CAAC,CAAC,CAAC;AACvB,wBAAgB,IAAI,CAAC,CAAC,EACpB,KAAK,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EACxB,iBAAiB,CAAC,EAAE,OAAO,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAClD,YAAY,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;yBAHjB,IAAI;kBAyCJ,CAAC,SAAS,SAAS,OAAO,EAAE,6BAEzC,YAAY,CAAC,CAAC,CAAC"}
|
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
|
-
|
|
154
|
-
|
|
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
|
-
|
|
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 &&
|
|
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.
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
package/dist/index.d.ts.map
CHANGED
|
@@ -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"}
|
package/dist/index.iife.js
CHANGED
|
@@ -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
|
-
|
|
155
|
-
|
|
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
|
-
|
|
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 &&
|
|
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.
|
|
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
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 &&
|
|
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.
|
|
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
|
-
|
|
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
|
|
42
|
-
var
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return
|
|
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.
|
|
93
|
+
exports.useSelector = useSelector;
|
|
50
94
|
exports.useSignal = useSignal;
|
package/dist/react.d.ts
CHANGED
package/dist/react.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
|
36
|
-
|
|
37
|
-
|
|
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,
|
|
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.
|
|
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
|
-
"
|
|
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",
|
package/dist/useSelectAtom.d.ts
DELETED
|
@@ -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"}
|