@toggld/react 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -45,7 +45,7 @@ A component re-renders only when **its** flag's result changes.
45
45
  ## Server-side rendering
46
46
 
47
47
  Create the client in the root component with `useState(() => new ToggldClient({
48
- …, bootstrap }))`, where `bootstrap` is `@toggld/node`'s `evaluateAll(context)`
48
+ …, bootstrap }))`, where `bootstrap` is `@toggld/js`'s `evaluateAll(context)`
49
49
  from the server. Server output and hydration match, and the browser sends no
50
50
  first request.
51
51
 
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createContext, createElement, useContext, useRef, useSyncExternalStore } from "react";
1
+ import { createContext, createElement, useContext, useEffect, useRef, useSyncExternalStore } from "react";
2
2
  import { ToggldClient } from "@toggld/browser";
3
3
  // src/index.ts
4
4
  /**
@@ -30,6 +30,9 @@ import { ToggldClient } from "@toggld/browser";
30
30
  * that did not change by identity, and the hooks compare by identity. Built on
31
31
  * `useSyncExternalStore`, so server rendering and hydration work when the
32
32
  * client is created with a `bootstrap`.
33
+ *
34
+ * For Analytics, a hook counts one read when its component mounts with a
35
+ * value, and one more each time its result changes, not one per render.
33
36
  */
34
37
  const ClientContext = createContext(null);
35
38
  ClientContext.displayName = "ToggldClient";
@@ -57,6 +60,8 @@ function useToggldStatus() {
57
60
  const getStatus = () => client.getSnapshot().status;
58
61
  return useSyncExternalStore(client.subscribe, getStatus, getStatus);
59
62
  }
63
+ const INTERNAL = Symbol.for("toggld.browser.internal");
64
+ const internalOf = (client) => client[INTERNAL];
60
65
  /** Evaluate with the getter that matches the default value's type. */
61
66
  const evaluateTyped = (client, key, defaultValue) => {
62
67
  switch (typeof defaultValue) {
@@ -79,14 +84,22 @@ const sameMetadata = (a, b) => {
79
84
  const sameDetails = (a, b) => a === b || a.flagKey === b.flagKey && Object.is(a.value, b.value) && a.variant === b.variant && a.reason === b.reason && a.errorCode === b.errorCode && a.errorMessage === b.errorMessage && sameMetadata(a.flagMetadata, b.flagMetadata);
80
85
  function useFlagDetails(key, defaultValue) {
81
86
  const client = useToggldClient();
87
+ const internal = internalOf(client);
82
88
  const cache = useRef(null);
89
+ const counted = useRef(null);
83
90
  const get = () => {
84
- const details = evaluateTyped(client, key, defaultValue);
91
+ const details = internal?.peek(key, defaultValue) ?? evaluateTyped(client, key, defaultValue);
85
92
  if (cache.current !== null && sameDetails(cache.current, details)) return cache.current;
86
93
  cache.current = details;
87
94
  return details;
88
95
  };
89
- return useSyncExternalStore(client.subscribe, get, get);
96
+ const details = useSyncExternalStore(client.subscribe, get, get);
97
+ useEffect(() => {
98
+ if (counted.current === details) return;
99
+ counted.current = details;
100
+ internal?.record(details);
101
+ }, [details, internal]);
102
+ return details;
90
103
  }
91
104
  function useFlag(key, defaultValue) {
92
105
  return useFlagDetails(key, defaultValue).value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toggld/react",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "React bindings for the Toggld browser SDK — a provider and hooks that re-render only when a flag's value changes. Server-rendering friendly.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://toggld.io",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "peerDependencies": {
38
38
  "react": "^18.0.0 || ^19.0.0",
39
- "@toggld/browser": "^0.1.0"
39
+ "@toggld/browser": "^0.2.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/react": "^19.3.0",
@@ -46,7 +46,7 @@
46
46
  "react-dom": "^19.3.1",
47
47
  "tsdown": "^0.23.2",
48
48
  "typescript": "6.0.3",
49
- "@toggld/browser": "0.1.0"
49
+ "@toggld/browser": "0.2.0"
50
50
  },
51
51
  "scripts": {
52
52
  "build": "tsdown",