flatsignals 0.2.0 → 0.2.2

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/README.md CHANGED
@@ -14,42 +14,42 @@
14
14
 
15
15
  ### 1-to-64 fanout
16
16
 
17
- | Library | Operations/sec ⚡ | vs flatsignals |
18
- | -------------------- | ----------------- | ---------------- |
19
- | **flatsignals** 🏆 | **449,848** | **baseline** |
20
- | alien-signals | 261,082 | **1.72x slower** |
21
- | @preact/signals | 230,899 | **1.95x slower** |
22
- | @reactively/core | 182,403 | **2.47x slower** |
23
- | @vue/reactivity | 152,709 | **2.95x slower** |
24
- | @maverick-js/signals | 137,712 | **3.27x slower** |
25
- | Angular Signals | 98,597 | **4.56x slower** |
26
- | @solidjs/signals | 78,849 | **5.71x slower** |
17
+ | Library | Operations/sec ⚡ | vs flatsignals |
18
+ | -------------------- | ----------------- | -------------- |
19
+ | **flatsignals** 🏆 | **449,848** | **baseline** |
20
+ | alien-signals | 261,082 | 1.72x slower |
21
+ | @preact/signals | 230,899 | 1.95x slower |
22
+ | @reactively/core | 182,403 | 2.47x slower |
23
+ | @vue/reactivity | 152,709 | 2.95x slower |
24
+ | @maverick-js/signals | 137,712 | 3.27x slower |
25
+ | Angular Signals | 98,597 | 4.56x slower |
26
+ | @solidjs/signals | 78,849 | 5.71x slower |
27
27
 
28
28
  ### High-frequency updates
29
29
 
30
- | Library | Operations/sec ⚡ | vs flatsignals |
31
- | -------------------- | ----------------- | ---------------- |
32
- | **flatsignals** 🏆 | **976,139** | **baseline** |
33
- | alien-signals | 502,513 | **1.94x slower** |
34
- | @preact/signals | 492,543 | **1.98x slower** |
35
- | @reactively/core | 438,882 | **2.22x slower** |
36
- | @maverick-js/signals | 343,368 | **2.84x slower** |
37
- | Angular Signals | 241,189 | **4.05x slower** |
38
- | @vue/reactivity | 221,537 | **4.41x slower** |
39
- | @solidjs/signals | 202,492 | **4.82x slower** |
30
+ | Library | Operations/sec ⚡ | vs flatsignals |
31
+ | -------------------- | ----------------- | -------------- |
32
+ | **flatsignals** 🏆 | **976,139** | **baseline** |
33
+ | alien-signals | 502,513 | 1.94x slower |
34
+ | @preact/signals | 492,543 | 1.98x slower |
35
+ | @reactively/core | 438,882 | 2.22x slower |
36
+ | @maverick-js/signals | 343,368 | 2.84x slower |
37
+ | Angular Signals | 241,189 | 4.05x slower |
38
+ | @vue/reactivity | 221,537 | 4.41x slower |
39
+ | @solidjs/signals | 202,492 | 4.82x slower |
40
40
 
41
41
  ### Diamond
42
42
 
43
- | Library | Operations/sec ⚡ | vs flatsignals |
44
- | -------------------- | ----------------- | ---------------- |
45
- | **flatsignals** 🏆 | **4,556,987** | **baseline** |
46
- | alien-signals | 3,028,320 | **1.50x slower** |
47
- | @preact/signals | 2,531,788 | **1.80x slower** |
48
- | @reactively/core | 1,688,053 | **2.70x slower** |
49
- | Angular Signals | 1,614,432 | **2.82x slower** |
50
- | @vue/reactivity | 1,563,865 | **2.91x slower** |
51
- | @maverick-js/signals | 1,407,975 | **3.24x slower** |
52
- | @solidjs/signals | 870,080 | **5.24x slower** |
43
+ | Library | Operations/sec ⚡ | vs flatsignals |
44
+ | -------------------- | ----------------- | -------------- |
45
+ | **flatsignals** 🏆 | **4,556,987** | **baseline** |
46
+ | alien-signals | 3,028,320 | 1.50x slower |
47
+ | @preact/signals | 2,531,788 | 1.80x slower |
48
+ | @reactively/core | 1,688,053 | 2.70x slower |
49
+ | Angular Signals | 1,614,432 | 2.82x slower |
50
+ | @vue/reactivity | 1,563,865 | 2.91x slower |
51
+ | @maverick-js/signals | 1,407,975 | 3.24x slower |
52
+ | @solidjs/signals | 870,080 | 5.24x slower |
53
53
 
54
54
  ## Installation
55
55
 
@@ -77,19 +77,17 @@ const log = effect(() => console.log(double.val));
77
77
  ## With React
78
78
 
79
79
  ```tsx
80
- import { useFlatSignal } from "flatsignals/react";
80
+ import { useFlatSignal, useFlatReader } from "flatsignals/react";
81
81
  import { counter, double } from "./signals";
82
82
 
83
83
  function MyCounter() {
84
- const [counter, setCounter] = useFlatSignal(0);
85
- return (
86
- <button onClick={() => setCounter(counter + 1)}>Count: {counter}</button>
87
- );
84
+ const [val, setVal] = useFlatSignal(counter);
85
+ return <button onClick={() => setVal(val + 1)}>Count: {val}</button>;
88
86
  }
89
87
 
90
88
  function ReadDouble() {
91
- const double = useFlatReader(double);
92
- return <div>{double}</div>;
89
+ const val = useFlatReader(double);
90
+ return <div>{val}</div>;
93
91
  }
94
92
  ```
95
93
 
package/dist/react.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { n as FlatRoot, r as FlatSignal, t as FlatCompute } from "./index-BUz21f61.js";
2
2
 
3
3
  //#region src/react/index.d.ts
4
- declare function useFlatReader<T>(signal: FlatSignal<T> | FlatCompute<T>): T;
4
+ declare function useFlatReader<T>(signal: FlatSignal<T> | FlatCompute<T>, isEqual?: (a: T, b: T) => boolean): T;
5
5
  declare function useFlatWriter<T>(signal: FlatSignal<T>): (val: T | ((oldVal: T) => T)) => void;
6
6
  declare function useFlatSignal<T>(signal: FlatSignal<T>): readonly [T, (val: T | ((oldVal: T) => T)) => void];
7
7
  declare function useFlatEffect(fn: () => undefined | (() => void)): void;
package/dist/react.js CHANGED
@@ -1,12 +1,16 @@
1
1
  import { o as effect, s as scoped } from "./src-CJj34M6F.js";
2
- import { useCallback, useEffect, useMemo, useSyncExternalStore } from "react";
2
+ import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
3
3
 
4
4
  //#region src/react/index.ts
5
- function useFlatReader(signal) {
5
+ function useFlatReader(signal, isEqual = Object.is) {
6
+ const lastValueRef = useRef(signal.peek);
6
7
  return useSyncExternalStore(useCallback((onStoreChange) => scoped(() => effect(() => {
7
- signal.val;
8
- onStoreChange();
9
- }), signal.root), [signal]), () => signal.val, () => signal.peek);
8
+ const newValue = signal.val;
9
+ if (!isEqual(lastValueRef.current, newValue)) {
10
+ lastValueRef.current = newValue;
11
+ onStoreChange();
12
+ }
13
+ }), signal.root), [signal, isEqual]), () => signal.val, () => signal.peek);
10
14
  }
11
15
  function useFlatWriter(signal) {
12
16
  return useCallback((val) => {
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","names":["cleanup: undefined | (() => void)"],"sources":["../src/react/index.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useSyncExternalStore } from \"react\";\nimport {\n\teffect,\n\ttype FlatCompute,\n\ttype FlatRoot,\n\ttype FlatSignal,\n\tscoped,\n} from \"../index.js\";\n\nexport function useFlatReader<T>(signal: FlatSignal<T> | FlatCompute<T>): T {\n\treturn useSyncExternalStore(\n\t\tuseCallback(\n\t\t\t(onStoreChange) =>\n\t\t\t\tscoped(\n\t\t\t\t\t() =>\n\t\t\t\t\t\teffect(() => {\n\t\t\t\t\t\t\tsignal.val; // track\n\t\t\t\t\t\t\tonStoreChange();\n\t\t\t\t\t\t}),\n\t\t\t\t\tsignal.root,\n\t\t\t\t),\n\t\t\t[signal],\n\t\t),\n\t\t() => signal.val,\n\t\t() => signal.peek,\n\t);\n}\n\nexport function useFlatWriter<T>(\n\tsignal: FlatSignal<T>,\n): (val: T | ((oldVal: T) => T)) => void {\n\treturn useCallback(\n\t\t(val) => {\n\t\t\tif (typeof val === \"function\") {\n\t\t\t\tsignal.val = (val as (oldVal: T) => T)(signal.peek);\n\t\t\t} else {\n\t\t\t\tsignal.val = val;\n\t\t\t}\n\t\t},\n\t\t[signal],\n\t);\n}\n\nexport function useFlatSignal<T>(signal: FlatSignal<T>) {\n\tconst reader = useFlatReader(signal);\n\tconst writer = useFlatWriter(signal);\n\treturn [reader, writer] as const;\n}\n\nexport function useFlatEffect(fn: () => undefined | (() => void)): void {\n\tuseEffect(() => {\n\t\tlet cleanup: undefined | (() => void);\n\t\tconst stop = effect(() => {\n\t\t\tif (cleanup) cleanup();\n\t\t\tcleanup = fn();\n\t\t});\n\n\t\treturn () => {\n\t\t\tif (cleanup) cleanup();\n\t\t\tstop();\n\t\t};\n\t}, [fn]);\n}\n\nexport function useFlatScope<T>(callback: () => T, scope?: FlatRoot): T {\n\treturn useMemo(() => scoped(callback, scope), [callback, scope]);\n}\n"],"mappings":";;;;AASA,SAAgB,cAAiB,QAA2C;AAC3E,QAAO,qBACN,aACE,kBACA,aAEE,aAAa;AACZ,SAAO;AACP,iBAAe;GACd,EACH,OAAO,KACP,EACF,CAAC,OAAO,CACR,QACK,OAAO,WACP,OAAO,KACb;;AAGF,SAAgB,cACf,QACwC;AACxC,QAAO,aACL,QAAQ;AACR,MAAI,OAAO,QAAQ,WAClB,QAAO,MAAO,IAAyB,OAAO,KAAK;MAEnD,QAAO,MAAM;IAGf,CAAC,OAAO,CACR;;AAGF,SAAgB,cAAiB,QAAuB;AAGvD,QAAO,CAFQ,cAAc,OAAO,EACrB,cAAc,OAAO,CACb;;AAGxB,SAAgB,cAAc,IAA0C;AACvE,iBAAgB;EACf,IAAIA;EACJ,MAAM,OAAO,aAAa;AACzB,OAAI,QAAS,UAAS;AACtB,aAAU,IAAI;IACb;AAEF,eAAa;AACZ,OAAI,QAAS,UAAS;AACtB,SAAM;;IAEL,CAAC,GAAG,CAAC;;AAGT,SAAgB,aAAgB,UAAmB,OAAqB;AACvE,QAAO,cAAc,OAAO,UAAU,MAAM,EAAE,CAAC,UAAU,MAAM,CAAC"}
1
+ {"version":3,"file":"react.js","names":["cleanup: undefined | (() => void)"],"sources":["../src/react/index.ts"],"sourcesContent":["import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport {\n effect,\n type FlatCompute,\n type FlatRoot,\n type FlatSignal,\n scoped,\n} from \"../index.js\";\n\nexport function useFlatReader<T>(\n signal: FlatSignal<T> | FlatCompute<T>,\n isEqual: (a: T, b: T) => boolean = Object.is\n): T {\n const lastValueRef = useRef<T>(signal.peek);\n\n return useSyncExternalStore(\n useCallback(\n (onStoreChange) =>\n scoped(\n () =>\n effect(() => {\n const newValue = signal.val;\n if (!isEqual(lastValueRef.current, newValue)) {\n lastValueRef.current = newValue;\n onStoreChange();\n }\n }),\n signal.root\n ),\n [signal, isEqual]\n ),\n () => signal.val,\n () => signal.peek\n );\n}\n\nexport function useFlatWriter<T>(\n signal: FlatSignal<T>\n): (val: T | ((oldVal: T) => T)) => void {\n return useCallback(\n (val) => {\n if (typeof val === \"function\") {\n signal.val = (val as (oldVal: T) => T)(signal.peek);\n } else {\n signal.val = val;\n }\n },\n [signal]\n );\n}\n\nexport function useFlatSignal<T>(signal: FlatSignal<T>) {\n const reader = useFlatReader(signal);\n const writer = useFlatWriter(signal);\n return [reader, writer] as const;\n}\n\nexport function useFlatEffect(fn: () => undefined | (() => void)): void {\n useEffect(() => {\n let cleanup: undefined | (() => void);\n const stop = effect(() => {\n if (cleanup) cleanup();\n cleanup = fn();\n });\n\n return () => {\n if (cleanup) cleanup();\n stop();\n };\n }, [fn]);\n}\n\nexport function useFlatScope<T>(callback: () => T, scope?: FlatRoot): T {\n return useMemo(() => scoped(callback, scope), [callback, scope]);\n}\n"],"mappings":";;;;AAeA,SAAgB,cACd,QACA,UAAmC,OAAO,IACvC;CACH,MAAM,eAAe,OAAU,OAAO,KAAK;AAE3C,QAAO,qBACL,aACG,kBACC,aAEI,aAAa;EACX,MAAM,WAAW,OAAO;AACxB,MAAI,CAAC,QAAQ,aAAa,SAAS,SAAS,EAAE;AAC5C,gBAAa,UAAU;AACvB,kBAAe;;GAEjB,EACJ,OAAO,KACR,EACH,CAAC,QAAQ,QAAQ,CAClB,QACK,OAAO,WACP,OAAO,KACd;;AAGH,SAAgB,cACd,QACuC;AACvC,QAAO,aACJ,QAAQ;AACP,MAAI,OAAO,QAAQ,WACjB,QAAO,MAAO,IAAyB,OAAO,KAAK;MAEnD,QAAO,MAAM;IAGjB,CAAC,OAAO,CACT;;AAGH,SAAgB,cAAiB,QAAuB;AAGtD,QAAO,CAFQ,cAAc,OAAO,EACrB,cAAc,OAAO,CACb;;AAGzB,SAAgB,cAAc,IAA0C;AACtE,iBAAgB;EACd,IAAIA;EACJ,MAAM,OAAO,aAAa;AACxB,OAAI,QAAS,UAAS;AACtB,aAAU,IAAI;IACd;AAEF,eAAa;AACX,OAAI,QAAS,UAAS;AACtB,SAAM;;IAEP,CAAC,GAAG,CAAC;;AAGV,SAAgB,aAAgB,UAAmB,OAAqB;AACtE,QAAO,cAAc,OAAO,UAAU,MAAM,EAAE,CAAC,UAAU,MAAM,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flatsignals",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "FlatSignals is an extremely fast reactivity library (~0.7kb)",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",