@raresportan/marram 0.2.1 → 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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/react/keyed.ts +17 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raresportan/marram",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "The gate, ratchet lints and journeys that keep agent-written code from drifting.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,19 +1,25 @@
1
- import { useCallback, useRef, useState } from "react";
1
+ import { useCallback, useReducer } from "react";
2
+
3
+ type Value<T> = T | ((previous: T) => T);
2
4
 
3
5
  /**
4
6
  * State that belongs to a key (a watch's parameters, a file, a document): under another key it reads as `initial`, so
5
7
  * nothing shows the last key's value while the new key's loads, not even for the one render an effect resetting it
6
8
  * would leave. Set it with the key the value is for, or with an update of that key's value; a late answer for a key
7
- * that's gone reads as `initial` too.
9
+ * that's gone is dropped, so it can't take the place of the current key's value. A key says only what a value is for:
10
+ * two requests under one key can still answer out of order, so a caller that re-reads puts a version or try in the key.
8
11
  */
9
- export function useKeyed<T>(key: string, initial: T): [T, (forKey: string, value: T | ((previous: T) => T)) => void] {
10
- const [held, setHeld] = useState<{ key: string; value: T }>({ key, value: initial });
11
- const first = useRef(initial);
12
- const set = useCallback((forKey: string, value: T | ((previous: T) => T)) => {
13
- setHeld((last) => {
14
- const previous = last.key === forKey ? last.value : first.current;
15
- return { key: forKey, value: typeof value === "function" ? (value as (previous: T) => T)(previous) : value };
16
- });
17
- }, []);
12
+ export function useKeyed<T>(key: string, initial: T): [T, (forKey: string, value: Value<T>) => void] {
13
+ // Written inline because React runs a reducer in the render an update lands in: this one sees that render's key, which
14
+ // `set`, called from a promise or a listener, can't know.
15
+ const [held, apply] = useReducer(
16
+ (last: { key: string; value: T }, { forKey, value }: { forKey: string; value: Value<T> }) => {
17
+ if (forKey !== key) return last;
18
+ const previous = last.key === key ? last.value : initial;
19
+ return { key, value: typeof value === "function" ? (value as (previous: T) => T)(previous) : value };
20
+ },
21
+ { key, value: initial },
22
+ );
23
+ const set = useCallback((forKey: string, value: Value<T>) => apply({ forKey, value }), []);
18
24
  return [held.key === key ? held.value : initial, set];
19
25
  }