@checkstack/ui 1.8.2 → 1.9.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.
@@ -0,0 +1,87 @@
1
+ import { useEffect, useRef } from "react";
2
+
3
+ /**
4
+ * Pure decision function powering {@link useInitOnceForKey}. Extracted so
5
+ * it can be unit-tested without a DOM (the hook itself wraps this with
6
+ * a `useRef` + `useEffect`).
7
+ *
8
+ * Returns `true` iff the caller should run their initialiser:
9
+ *
10
+ * - `value` is defined (the query has finished loading), AND
11
+ * - `key` is defined (we have a discriminator to track init-per-record),
12
+ * AND
13
+ * - we haven't yet initialised for this key (i.e. `initialisedKey !== key`).
14
+ *
15
+ * Background refetches of the same record produce a new `value` reference
16
+ * but the same `key`, so the function returns `false` for them — that's
17
+ * the whole point.
18
+ */
19
+ export function shouldInitForKey({
20
+ value,
21
+ key,
22
+ initialisedKey,
23
+ }: {
24
+ value: unknown;
25
+ key: string | number | null | undefined;
26
+ initialisedKey: string | number | null | undefined;
27
+ }): boolean {
28
+ if (value === undefined || value === null) return false;
29
+ if (key === undefined || key === null) return false;
30
+ return initialisedKey !== key;
31
+ }
32
+
33
+ /**
34
+ * Run a one-shot initialiser exactly once per `key`, ignoring subsequent
35
+ * `value` changes that keep the same key.
36
+ *
37
+ * Built for forms that need to seed local state from a react-query result
38
+ * but **must not** reset that state when the query refetches in the
39
+ * background. The canonical use case is the healthcheck editor: a realtime
40
+ * `HEALTH_CHECK_RUN_COMPLETED` signal invalidates the configuration query
41
+ * on every run, which would otherwise wipe the user's in-progress edits
42
+ * via a naive `useEffect([data], () => setState(data))`.
43
+ *
44
+ * Behaviour:
45
+ * - Calls `onInit(value)` the first time `value` is defined for a given
46
+ * `key`.
47
+ * - **Does NOT** call it again if `value` changes but `key` stays the
48
+ * same. Background refetches keep the same key (= the same record's
49
+ * primary id) and therefore don't re-run the initialiser.
50
+ * - **Does** call it again when `key` changes — e.g. when the user
51
+ * navigates to a different record without unmounting the page.
52
+ * - Skips initialisation entirely while either `value` or `key` is
53
+ * `undefined`/`null`.
54
+ *
55
+ * `onInit` is read from a ref so callers can safely pass a fresh closure
56
+ * each render without re-firing the effect.
57
+ *
58
+ * @example
59
+ * useInitOnceForKey(existingConfig, existingConfig?.id, (config) => {
60
+ * setFormState({
61
+ * name: config.name,
62
+ * collectors: config.collectors ?? [],
63
+ * });
64
+ * });
65
+ */
66
+ export function useInitOnceForKey<T>(
67
+ value: T | undefined | null,
68
+ key: string | number | null | undefined,
69
+ onInit: (value: T) => void,
70
+ ): void {
71
+ const initialisedKeyRef = useRef<string | number | null | undefined>(undefined);
72
+ const onInitRef = useRef(onInit);
73
+
74
+ // Keep the latest callback in a ref so a fresh closure each render doesn't
75
+ // re-trigger the effect; only `value` and `key` should drive it.
76
+ useEffect(() => {
77
+ onInitRef.current = onInit;
78
+ });
79
+
80
+ useEffect(() => {
81
+ if (!shouldInitForKey({ value, key, initialisedKey: initialisedKeyRef.current })) {
82
+ return;
83
+ }
84
+ initialisedKeyRef.current = key;
85
+ onInitRef.current(value as T);
86
+ }, [value, key]);
87
+ }
package/src/index.ts CHANGED
@@ -63,3 +63,4 @@ export * from "./components/MetricTile";
63
63
  export * from "./components/Sheet";
64
64
  export * from "./components/Popover";
65
65
  export * from "./hooks/useIsMobile";
66
+ export * from "./hooks/useInitOnceForKey";
package/tsconfig.json CHANGED
@@ -1,7 +1,9 @@
1
1
  {
2
2
  "extends": "@checkstack/tsconfig/frontend.json",
3
3
  "include": [
4
- "src"
4
+ "src",
5
+ "src/components/CodeEditor/generated/stdlib-types.json",
6
+ "scripts"
5
7
  ],
6
8
  "references": [
7
9
  {