@pond-ts/react 0.5.5 → 0.5.7

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.
@@ -26,6 +26,13 @@ export interface UseCurrentOptions extends UseSnapshotOptions {
26
26
  * Returns a stable-shape object while the source has no events (every
27
27
  * mapped field is `undefined`), so destructuring on first render is
28
28
  * safe.
29
+ *
30
+ * **Reference stability**: when a new event push leaves the reduce
31
+ * output structurally unchanged (same scalar values, same-length arrays
32
+ * with same elements), the previous result reference is returned
33
+ * unchanged. Downstream `useMemo([value])` and `useEffect([value])` only
34
+ * re-run when the value actually changes — no need for a manual
35
+ * `.slice()` or deep-compare equality helper at the call site.
29
36
  */
30
37
  export declare function useCurrent<S extends SeriesSchema, const Mapping extends AggregateMap<S>>(source: SnapshotSource<S> | LiveSource<S> | null, mapping: Mapping, options?: UseCurrentOptions): ReduceResult<S, Mapping>;
31
38
  //# sourceMappingURL=useCurrent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCurrent.d.ts","sourceRoot":"","sources":["../src/useCurrent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,YAAY,EACb,MAAM,SAAS,CAAC;AACjB,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACxB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D;;;;OAIG;IACH,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CACxB,CAAC,SAAS,YAAY,EACtB,KAAK,CAAC,OAAO,SAAS,YAAY,CAAC,CAAC,CAAC,EAErC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,EAChD,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAe1B"}
1
+ {"version":3,"file":"useCurrent.d.ts","sourceRoot":"","sources":["../src/useCurrent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,UAAU,EACV,YAAY,EACZ,YAAY,EACb,MAAM,SAAS,CAAC;AACjB,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACxB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,iBAAkB,SAAQ,kBAAkB;IAC3D;;;;OAIG;IACH,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAuED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CACxB,CAAC,SAAS,YAAY,EACtB,KAAK,CAAC,OAAO,SAAS,YAAY,CAAC,CAAC,CAAC,EAErC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,EAChD,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CA8B1B"}
@@ -1,5 +1,69 @@
1
- import { useMemo } from 'react';
1
+ import { useMemo, useRef } from 'react';
2
2
  import { useSnapshot, } from './useSnapshot.js';
3
+ /**
4
+ * Returns `true` if two array cells have identical length and
5
+ * elementwise-equal contents. Element types are always scalars (the
6
+ * reducer registry enforces this), so `===` per element is both
7
+ * correct and cheap for the sizes a dashboard produces.
8
+ */
9
+ function arraysEqual(a, b) {
10
+ if (a.length !== b.length)
11
+ return false;
12
+ for (let i = 0; i < a.length; i += 1) {
13
+ if (a[i] !== b[i])
14
+ return false;
15
+ }
16
+ return true;
17
+ }
18
+ /**
19
+ * Field-level structural stabilization for a `useCurrent` result.
20
+ *
21
+ * Walks the next result keys and, for any field whose value is
22
+ * structurally equal to the previous render's value, reuses the
23
+ * previous reference. If every field reuses the previous reference
24
+ * AND the key set is unchanged, returns the previous top-level
25
+ * object as-is. Otherwise, returns a fresh object whose stable fields
26
+ * still carry their previous reference.
27
+ *
28
+ * Result: downstream `useMemo([current.host])` / `useEffect` keyed
29
+ * off a specific field only re-fire when that field changes — not
30
+ * when a sibling field changes, and not when a new event push leaves
31
+ * the aggregate unchanged.
32
+ */
33
+ function stabilizeFields(prev, next) {
34
+ if (prev === null)
35
+ return next;
36
+ const prevKeys = Object.keys(prev);
37
+ const nextKeys = Object.keys(next);
38
+ if (prevKeys.length !== nextKeys.length)
39
+ return next;
40
+ const result = {};
41
+ let anyFieldChanged = false;
42
+ for (const key of nextKeys) {
43
+ if (!Object.prototype.hasOwnProperty.call(prev, key))
44
+ return next;
45
+ const prevValue = prev[key];
46
+ const nextValue = next[key];
47
+ if (prevValue === nextValue) {
48
+ result[key] = prevValue;
49
+ continue;
50
+ }
51
+ if (Array.isArray(prevValue) &&
52
+ Array.isArray(nextValue) &&
53
+ arraysEqual(prevValue, nextValue)) {
54
+ // Arrays with identical contents — reuse the previous reference
55
+ // so field-level dependency arrays stay stable.
56
+ result[key] = prevValue;
57
+ continue;
58
+ }
59
+ result[key] = nextValue;
60
+ anyFieldChanged = true;
61
+ }
62
+ // Every field reused the previous reference — return the original
63
+ // top-level object so the whole-result consumer (`useEffect([current])`)
64
+ // also stays stable.
65
+ return anyFieldChanged ? result : prev;
66
+ }
3
67
  /**
4
68
  * Subscribe to a live source and return the current value of a reducer
5
69
  * mapping, updated on a throttle. Equivalent to
@@ -18,11 +82,19 @@ import { useSnapshot, } from './useSnapshot.js';
18
82
  * Returns a stable-shape object while the source has no events (every
19
83
  * mapped field is `undefined`), so destructuring on first render is
20
84
  * safe.
85
+ *
86
+ * **Reference stability**: when a new event push leaves the reduce
87
+ * output structurally unchanged (same scalar values, same-length arrays
88
+ * with same elements), the previous result reference is returned
89
+ * unchanged. Downstream `useMemo([value])` and `useEffect([value])` only
90
+ * re-run when the value actually changes — no need for a manual
91
+ * `.slice()` or deep-compare equality helper at the call site.
21
92
  */
22
93
  export function useCurrent(source, mapping, options) {
23
94
  const snap = useSnapshot(source, options);
24
95
  const tailOpt = options?.tail;
25
- return useMemo(() => {
96
+ const previousResultRef = useRef(null);
97
+ const nextResult = useMemo(() => {
26
98
  if (!snap) {
27
99
  // Stable empty-shape result so destructuring never explodes on
28
100
  // first render.
@@ -34,5 +106,14 @@ export function useCurrent(source, mapping, options) {
34
106
  const scoped = tailOpt !== undefined ? snap.tail(tailOpt) : snap;
35
107
  return scoped.reduce(mapping);
36
108
  }, [snap, tailOpt, mapping]);
109
+ // Field-level structural stabilization. Every field whose value is
110
+ // unchanged reuses its previous reference; the top-level object
111
+ // reuses its reference when *all* fields are stable. Downstream
112
+ // `useMemo([current.host])` keyed off a specific field only re-runs
113
+ // when that field actually changes, and `useEffect([current])` keyed
114
+ // off the whole result only fires on true change.
115
+ const stabilized = stabilizeFields(previousResultRef.current, nextResult);
116
+ previousResultRef.current = stabilized;
117
+ return stabilized;
37
118
  }
38
119
  //# sourceMappingURL=useCurrent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCurrent.js","sourceRoot":"","sources":["../src/useCurrent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAQhC,OAAO,EACL,WAAW,GAGZ,MAAM,kBAAkB,CAAC;AAW1B;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,UAAU,CAIxB,MAAgD,EAChD,OAAgB,EAChB,OAA2B;IAE3B,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,OAAO,EAAE,IAAI,CAAC;IAE9B,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,+DAA+D;YAC/D,gBAAgB;YAChB,MAAM,KAAK,GAA4B,EAAE,CAAC;YAC1C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;YAC/D,OAAO,KAAiC,CAAC;QAC3C,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACjE,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,CAA6B,CAAC;IAC5D,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAC/B,CAAC"}
1
+ {"version":3,"file":"useCurrent.js","sourceRoot":"","sources":["../src/useCurrent.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pond-ts/react",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
4
4
  "description": "React hooks for pond-ts live time series",
5
5
  "license": "MIT",
6
6
  "repository": {