From 7eaae86a0fd444ca66e3c247ce0e79ca03b55ef2 Mon Sep 17 00:00:00 2001 From: Petyo Ivanov <underlog@gmail.com> Date: Mon, 25 Dec 2023 16:41:00 +0200 Subject: [PATCH] fix: distinct is true by default --- README.md | 12 ++++++------ src/examples.stories.tsx | 4 ++-- src/readme.stories.tsx | 4 ++-- src/realm.test.ts | 22 +++++++++++++--------- src/realm.ts | 16 ++++++++-------- 5 files changed, 31 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index b360f41..c2b4d88 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Welcome to the README of Gurx, an extensible typescript-native reactive state ma - **Open** - the node definition based approach lets you extend the logic of the state management by connecting more nodes and interactions to the existing ones. This lets you partition your state management logic into smaller, more manageable pieces and even build a plugin system on top of it. -- **Optimized** - any Gurx node can be marked as distinct, meaning that it will push through its subscribers only when a new value arrives. This allows you to avoid expensive computations and re-renders. +- **Optimized** - any Gurx node are marked as distinct by default, meaning that it will push through its subscribers only when a value different than the previous one is published. This allows you to avoid expensive computations and re-renders. - **Multi publish/subscribe** - you can subscribe to multiple nodes at once, and you can publish to multiple nodes at once. A multi-push will execute a single traversal of the node graph, and will re-render the components only once, given that they are subscribed through a single point. @@ -48,20 +48,18 @@ Note: You can name the node references with a dollar sign suffix, to indicate th const myCell$ = Cell( // initial value 0, - // distinct flag - true, // the r is the realm instance that starts the cell (r) => { r.sub(myCell$, (value) => { console.log('myCell$ changed to', value) }) } + // distinct flag, true by default + true ) // Since signals have no initial value, you need to specify the type of data that will flow through them const mySignal$ = Signal<number>( - // distinct flag - true, // the r is the realm instance that starts the cell (r) => { r.sub(mySignal$, (value) => { @@ -69,7 +67,9 @@ const mySignal$ = Signal<number>( }) // publishing a value through a signal will publish it into $myCell as well r.link(mySignal$, myCell$) - } + }, + // distinct flag + true ) ``` diff --git a/src/examples.stories.tsx b/src/examples.stories.tsx index e08625c..9ad8833 100644 --- a/src/examples.stories.tsx +++ b/src/examples.stories.tsx @@ -1,7 +1,7 @@ import { Action, Cell, RealmProvider, useCellValue, useCellValues, usePublisher } from '.' -const foo$ = Cell('foo', true) -const bar$ = Cell('bar', true) +const foo$ = Cell('foo') +const bar$ = Cell('bar') const q$ = Action((r) => { r.sub(q$, () => { diff --git a/src/readme.stories.tsx b/src/readme.stories.tsx index ef6acc6..412ff44 100644 --- a/src/readme.stories.tsx +++ b/src/readme.stories.tsx @@ -1,10 +1,10 @@ import { map, Signal, Cell, RealmProvider, useCellValue, usePublisher } from '.' // Create a cell with an initial value -const cell$ = Cell('foo', true) +const cell$ = Cell('foo') // A signal, that will update the cell when its value changes -const signal$ = Signal<number>(true, (r) => { +const signal$ = Signal<number>((r) => { r.link( r.pipe( signal$, diff --git a/src/realm.test.ts b/src/realm.test.ts index 67d83fe..6f8ed69 100644 --- a/src/realm.test.ts +++ b/src/realm.test.ts @@ -42,7 +42,7 @@ describe('gurx cells/signals', () => { it('supports init function for cells', () => { const a = Cell(2) - const b = Cell(2, true, (r) => { + const b = Cell(2, (r) => { r.link(b, a) }) r.pub(b, 3) @@ -51,7 +51,7 @@ describe('gurx cells/signals', () => { it('supports init function for signals', () => { const a = Cell(2) - const b = Signal(true, (r) => { + const b = Signal((r) => { r.link(b, a) }) r.pub(b, 3) @@ -92,9 +92,9 @@ describe('realm features', () => { }) it('supports undefined initial value', () => { - const n = Cell<string | undefined>(undefined, true) - const q = Cell(1, true) - const tc = Cell<number>(0, true) + const n = Cell<string | undefined>(undefined) + const q = Cell(1) + const tc = Cell<number>(0) r.link( r.pipe( r.combine(n, q), @@ -411,7 +411,7 @@ describe('realm features', () => { }) it('does not recall subscriptions for distinct stateful nodes', () => { - const a = Cell('foo', true) + const a = Cell('foo') const spy = vi.fn() r.sub(a, spy) r.pub(a, 'foo') @@ -421,7 +421,7 @@ describe('realm features', () => { it('does not recall subscriptions for distinct stateful child nodes', () => { const a = Cell('bar') - const b = Cell('foo', true) + const b = Cell('foo') const spy = vi.fn() r.connect({ map: (value) => value, @@ -436,7 +436,11 @@ describe('realm features', () => { }) it('supports custom comparator when distinct flag is set', () => { - const a = Cell({ id: 'foo' }, (current, next) => (current !== undefined ? current.id === next.id : false)) + const a = Cell( + { id: 'foo' }, + () => {}, + (current, next) => (current !== undefined ? current.id === next.id : false) + ) const spy = vi.fn() r.sub(a, spy) r.pub(a, { id: 'foo' }) @@ -446,7 +450,7 @@ describe('realm features', () => { it('supports subscribing to multiple nodes', () => { const a = Cell('bar') - const b = Cell('foo', true) + const b = Cell('foo') const spy = vi.fn() r.connect({ map: (value) => value, diff --git a/src/realm.ts b/src/realm.ts index d64cf94..ec3e261 100644 --- a/src/realm.ts +++ b/src/realm.ts @@ -111,10 +111,10 @@ export class Realm { * Creates or resolves an existing cell instance in the realm. Useful as a joint point when building your own operators. * @returns a reference to the cell. * @param value - the initial value of the cell - * @param distinct - false by default. Pass true to mark the cell as a distinct one, or a custom comparator in case of a non-primitive value. + * @param distinct - true by default. Pass false to mark the signal as a non-distinct one, meaning that publishing the same value multiple times will re-trigger a recomputation cycle. * @param node - optional, a reference to a cell. If the cell has not been touched in the realm before, the realm will instantiate a reference to it. If it's registered already, the function will return the reference. */ - cellInstance<T>(value: T, distinct: Distinct<T> = false, node = Symbol()): NodeRef<T> { + cellInstance<T>(value: T, distinct: Distinct<T> = true, node = Symbol()): NodeRef<T> { if (!this.state.has(node)) { this.state.set(node, value) if (distinct !== false) { @@ -128,10 +128,10 @@ export class Realm { /** * Creates or resolves an existing signal instance in the realm. Useful as a joint point when building your own operators. * @returns a reference to the signal. - * @param distinct - false by default. Pass true to mark the signal as a distinct one, or a custom comparator in case of a non-primitive value. + * @param distinct - true by default. Pass false to mark the signal as a non-distinct one, meaning that publishing the same value multiple times will re-trigger a recomputation cycle. * @param node - optional, a reference to a signal. If the signal has not been touched in the realm before, the realm will instantiate a reference to it. If it's registered already, the function will return the reference. */ - signalInstance<T>(distinct: Distinct<T> = false, node = Symbol()): NodeRef<T> { + signalInstance<T>(distinct: Distinct<T> = true, node = Symbol()): NodeRef<T> { if (distinct !== false) { this.distinctNodes.set(node, distinct === true ? defaultComparator : (distinct as Comparator<unknown>)) } @@ -671,8 +671,8 @@ export class Realm { * Defines a new **stateful node** and returns a reference to it. * Once a realm instance publishes or subscribes to the node, an instance of that node it will be registered in the realm. * @param value - the initial value of the node. Stateful nodes always have a value. - * @param distinct - if true, the node will only emit values that are different from the previous value. Optionally, a custom distinct function can be provided if the node values are non-primitive. * @param init - an optional function that will be called when the node is registered in a realm. Can be used to create subscriptions and define relationships to other nodes. Any referred nodes will be registered in the realm automatically. + * @param distinct - if true, the node will only emit values that are different from the previous value. Optionally, a custom distinct function can be provided if the node values are non-primitive. * @example * ```ts * const foo$ = Cell('foo', true, (r) => { @@ -685,7 +685,7 @@ export class Realm { * If you need to get the current value of a stateful node, use {@link Realm.getValue}. * @category Nodes */ -export function Cell<T>(value: T, distinct: Distinct<T> = false, init: (r: Realm) => void = noop): NodeRef<T> { +export function Cell<T>(value: T, init: (r: Realm) => void = noop, distinct: Distinct<T> = true): NodeRef<T> { return tap(Symbol(), (id) => { nodeDefs$$.set(id, { type: 'cell', distinct, initial: value, init }) }) as NodeRef<T> @@ -694,8 +694,8 @@ export function Cell<T>(value: T, distinct: Distinct<T> = false, init: (r: Realm /** * Defines a new **stateless node** and returns a reference to it. * Once a realm instance publishes or subscribes to the node, an instance of that node it will be registered in the realm. - * @param distinct - if true, the node will only emit values that are different from the previous value. Optionally, a custom distinct function can be provided if the node values are non-primitive. * @param init - an optional function that will be called when the node is registered in a realm. Can be used to create subscriptions and define relationships to other nodes. Any referred nodes will be registered in the realm automatically. + * @param distinct - true by default. The node emits values that are different from the previous value. Optionally, a custom distinct function can be provided if the node values are non-primitive. * @example * ```ts * const foo$ = Signal<string>(true, (r) => { @@ -706,7 +706,7 @@ export function Cell<T>(value: T, distinct: Distinct<T> = false, init: (r: Realm * ``` * @category Nodes */ -export function Signal<T>(distinct: Distinct<T> = false, init: NodeInit<T> = noop): NodeRef<T> { +export function Signal<T>(init: NodeInit<T> = noop, distinct: Distinct<T> = false): NodeRef<T> { return tap(Symbol(), (id) => { nodeDefs$$.set(id, { type: 'signal', distinct, init }) }) as NodeRef<T>