react-state-basis 0.3.4 → 0.4.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
@@ -11,7 +11,7 @@
11
11
  [![GitHub stars](https://img.shields.io/github/stars/liovic/react-state-basis.svg?style=flat-square)](https://github.com/liovic/react-state-basis/stargazers)
12
12
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
13
13
 
14
- **Catches redundant state and update chains while your React app runs.**
14
+ **Catches redundant state and update chains using temporal cross-correlation.**
15
15
 
16
16
  </div>
17
17
 
@@ -26,7 +26,7 @@
26
26
  - **Infinite loops** - Circular dependencies that freeze your browser
27
27
  - **Tight coupling** - State variables that should be independent but aren't
28
28
 
29
- It works by tracking *when* state updates happen, not *what* the values are.
29
+ It works by tracking *when* state updates happen (temporal patterns), not *what* the values are.
30
30
 
31
31
  ---
32
32
 
@@ -37,7 +37,7 @@ const [user, setUser] = useState(null);
37
37
  const [isLoggedIn, setIsLoggedIn] = useState(false);
38
38
 
39
39
  useEffect(() => {
40
- setIsLoggedIn(!!user); // Double render - flagged as redundant
40
+ setIsLoggedIn(!!user); // Double render - flagged as sync leak
41
41
  }, [user]);
42
42
 
43
43
  // ✅ Better
@@ -49,7 +49,7 @@ const isLoggedIn = !!user; // Computed, no second render
49
49
 
50
50
  ## See It Work
51
51
 
52
- The optional HUD shows which states update together in real-time:
52
+ The optional HUD shows your state basis matrix in real-time:
53
53
 
54
54
  <p align="center">
55
55
  <img src="./assets/react-state-basis.gif" width="800" alt="React State Basis Demo" />
@@ -57,6 +57,32 @@ The optional HUD shows which states update together in real-time:
57
57
 
58
58
  ---
59
59
 
60
+ ## Real-World Audits
61
+
62
+ Basis has been tested on major open-source projects to validate detection accuracy:
63
+
64
+ ### Excalidraw (114k+ ⭐)
65
+ **Detected:** Causal Sync Leak in theme state synchronization
66
+ **Issue:** A `useEffect` was manually syncing theme state, causing an unnecessary double render on every theme toggle
67
+ **Fix:** [PR #10637](https://github.com/excalidraw/excalidraw/pull/10637) - Replaced with a computed value
68
+
69
+ <p align="center">
70
+ <img src="./assets/excalidraw-audit.png" width="800" alt="Excalidraw Audit" />
71
+ </p>
72
+
73
+ ### shadcn-admin (10k+ ⭐)
74
+ **Detected:** Redundant state pattern in mobile detection hooks
75
+ **Issue:** Viewport resize events were being synchronized via effects rather than direct subscriptions
76
+ **Fix:** [PR #274](https://github.com/satnaing/shadcn-admin/pull/274) - Optimized subscription pattern
77
+
78
+ <p align="center">
79
+ <img src="./assets/shadcn-admin.png" width="800" alt="shadcn Admin Audit" />
80
+ </p>
81
+
82
+ > **Note:** These are proposed architectural improvements. Basis points out patterns worth investigating - the final decision rests with the maintainer.
83
+
84
+ ---
85
+
60
86
  ## Setup (Vite)
61
87
 
62
88
  ### 1. Install
@@ -91,7 +117,19 @@ root.render(
91
117
  );
92
118
  ```
93
119
 
94
- That's it. The tool runs automatically in development.
120
+ ### 4. Verify It's Working
121
+
122
+ Add this test pattern to any component:
123
+ ```tsx
124
+ const [a, setA] = useState(0);
125
+ const [b, setB] = useState(0);
126
+
127
+ useEffect(() => {
128
+ setB(a); // Basis will flag this
129
+ }, [a]);
130
+ ```
131
+
132
+ Trigger an update (e.g., click a button that calls `setA(1)`). You should see a console alert within ~100ms.
95
133
 
96
134
  ---
97
135
 
@@ -99,213 +137,297 @@ That's it. The tool runs automatically in development.
99
137
 
100
138
  ### Console Alerts
101
139
 
102
- When Basis detects issues, you'll see styled console logs:
103
-
104
- **Redundant State:**
140
+ **Redundant Pattern:**
141
+ Detected when two variables move in perfect unison.
105
142
  ```
106
- 📐 BASIS | REDUNDANT STATE DETECTED
143
+ 📐 BASIS | REDUNDANT PATTERN
107
144
  📍 Location: TodoList.tsx
108
- Pattern: States "todos" and "count" update together 92% of the time.
109
- One is likely redundant and can be removed.
110
-
111
- Suggested fix: Convert "count" to a computed value
145
+ Observation: "todos" and "count" move together.
146
+ One is likely a direct mirror of the other. Confidence: 94%
147
+ Action: Refactor "count" to useMemo.
112
148
  ```
113
149
 
114
- **Update Chains:**
150
+ **Sync Leak (Causal Chain):**
151
+ Detected when one variable consistently triggers another with a temporal lag.
115
152
  ```
116
- 💡 BASIS | UPDATE CHAIN DETECTED
117
- Sequence: user ➔ Effect ➔ isLoggedIn
118
- Pattern: "isLoggedIn" is synchronized via useEffect, causing a second render.
153
+ 💡 BASIS | DETECTED SYNC LEAK
154
+ 📍 Location: AuthProvider.tsx
155
+ Flow: user Effect isLoggedIn
156
+ Context: The engine detected a consistent 20ms lag between these updates.
157
+ Result: This creates a Double Render Cycle.
119
158
  ```
120
159
 
121
- **Infinite Loops:**
160
+ **Infinite Loop:**
161
+ Detected when a variable updates too rapidly (circuit breaker).
122
162
  ```
123
- 🛑 BASIS | INFINITE LOOP DETECTED
124
- State variable "counter" is updating too rapidly (25+ times in 500ms).
125
- Execution halted to prevent browser freeze.
163
+ 🛑 BASIS CRITICAL | CIRCUIT BREAKER
164
+ Infinite oscillation detected on: "counter"
165
+ Execution halted to prevent browser thread lock.
126
166
  ```
127
167
 
128
168
  ### Health Report
129
169
 
130
- Check your entire app's state health:
170
+ Check your entire app's state architecture:
131
171
  ```tsx
132
172
  window.printBasisReport();
133
173
  ```
134
174
 
135
175
  Shows:
136
- - Efficiency score (how much redundant state you have)
137
- - Which states are independent vs synchronized
138
- - Correlation matrix
176
+ - **Health Score** - Percentage of independent vs. synchronized state
177
+ - **Synchronized Clusters** - Groups of variables that move together
178
+ - **Correlation Matrix** - Full pairwise similarity analysis (for <15 variables)
139
179
 
140
180
  ---
141
181
 
142
- ## Testing on Real Projects
182
+ ## How It Works (v0.4.0)
143
183
 
144
- I've tested this on a few open-source projects to validate the detection:
184
+ ### Temporal Cross-Correlation
145
185
 
146
- ### Excalidraw (114k+ ⭐)
186
+ Basis tracks **when** state updates occur, creating a 50-tick timeline for each variable:
187
+ ```
188
+ useState("count"): [0,0,1,0,0,1,1,0,...] (updates at ticks 2, 5, 6)
189
+ useState("total"): [0,0,1,0,0,1,1,0,...] (same pattern)
190
+ ↑ Redundant: identical temporal signature
191
+ ```
147
192
 
148
- **Found:** Theme state being manually synchronized in `useEffect`
149
- **Issue:** Double render on every theme change
150
- **Fix:** [PR #10637](https://github.com/excalidraw/excalidraw/pull/10637) - replaced with computed value
151
- **Status:** Pending review
193
+ For every pair of variables, Basis checks three temporal relationships:
152
194
 
153
- <p align="center">
154
- <img src="./assets/excalidraw-audit.png" width="800" alt="Excalidraw Audit" />
155
- </p>
195
+ 1. **Synchronous (Redundancy):** Do they update in the same tick?
196
+ ```
197
+ A: [0,1,0,1,0,...]
198
+ B: [0,1,0,1,0,...] → Flagged as redundant
199
+ ```
156
200
 
157
- ### shadcn-admin (10k+ )
201
+ 2. **Lead-Lag (A → B):** Does B consistently follow A in the next tick?
202
+ ```
203
+ A: [0,1,0,1,0,...]
204
+ B: [0,0,1,0,1,...] → B follows A (sync leak detected)
205
+ ```
158
206
 
159
- **Found:** Mobile detection hook with effect-based synchronization
160
- **Issue:** Unnecessary re-renders on viewport resize
161
- **Fix:** [PR #274](https://github.com/satnaing/shadcn-admin/pull/274) - cleaner subscription pattern
162
- **Status:** Pending review
207
+ 3. **Lead-Lag (B → A):** Does A consistently follow B?
208
+ ```
209
+ A: [0,0,1,0,1,...]
210
+ B: [0,1,0,1,0,...] → A follows B (reverse causality)
211
+ ```
163
212
 
164
- <p align="center">
165
- <img src="./assets/shadcn-admin.png" width="800" alt="shadcn Admin Audit" />
166
- </p>
213
+ The engine uses **offset-based comparison** to check these patterns without allocating temporary arrays, ensuring minimal overhead even at high frame rates.
167
214
 
168
- > **Note:** These are proposed improvements based on the tool's detection. The maintainers may choose different solutions or determine the patterns are intentional.
215
+ ### Performance Optimizations
169
216
 
170
- ---
217
+ - **Idle Filtering:** Only analyzes variables with 2+ updates, reducing pairwise comparisons by ~90% in typical applications (measured on Excalidraw's 47-hook codebase)
218
+ - **Batched Analysis:** Runs every 5 ticks (~100ms) to avoid impacting frame budget
219
+ - **Console Throttling:** Same alert won't repeat within 5 seconds
220
+ - **Zero Production Overhead:** Entire library is replaced with no-op shims in production builds
171
221
 
172
- ## How It Works
222
+ ### What Gets Flagged?
173
223
 
174
- ### The Basic Idea
224
+ **Redundant Pattern Example:**
225
+ ```tsx
226
+ // ❌ Before (Basis flags this)
227
+ const [count, setCount] = useState(0);
228
+ const [doubled, setDoubled] = useState(0);
175
229
 
176
- The tool records the last 50 "ticks" (roughly 1 second) for each state variable:
177
- ```
178
- useState("count"): [0,1,0,0,1,1,0,0,0,...]
179
- useState("total"): [0,1,0,0,1,1,0,0,0,...]
180
- Both update at same times = probably redundant
230
+ useEffect(() => {
231
+ setDoubled(count * 2);
232
+ }, [count]);
233
+
234
+ // After (Basis suggestion)
235
+ const [count, setCount] = useState(0);
236
+ const doubled = useMemo(() => count * 2, [count]);
181
237
  ```
182
238
 
183
- It uses **cosine similarity** (a standard correlation metric) to detect when states update together.
239
+ **Sync Leak Example:**
240
+ ```tsx
241
+ // ❌ Before (causes double render)
242
+ const [user, setUser] = useState(null);
243
+ const [isLoggedIn, setIsLoggedIn] = useState(false);
184
244
 
185
- If similarity > 0.88, it flags them as potentially redundant.
245
+ useEffect(() => {
246
+ setIsLoggedIn(!!user);
247
+ }, [user]);
186
248
 
187
- ### Why This Works
249
+ // After (single render)
250
+ const [user, setUser] = useState(null);
251
+ const isLoggedIn = !!user; // Derived, no effect needed
252
+ ```
188
253
 
189
- Most architectural issues create **temporal patterns**:
190
- - Redundant state → always updates together
191
- - Update chains → one state updates, then another follows
192
- - Infinite loops → same state updates rapidly
254
+ ---
193
255
 
194
- The tool watches for these patterns and alerts you.
256
+ ## Upgrading from v0.3.x
195
257
 
196
- ### What It Doesn't Do
258
+ ### Breaking Changes
259
+ None! v0.4.0 is a drop-in replacement.
197
260
 
198
- Doesn't analyze your code statically
199
- Doesn't read variable values
200
- Doesn't prove mathematical correctness
201
- Doesn't replace code review
261
+ ### What's Different
262
+ - **Better Detection:** Temporal analysis reduces false positives by distinguishing redundancy from causality
263
+ - **New Alert Type:** "DETECTED SYNC LEAK" identifies effect-driven update chains with directional insight
264
+ - **Console Throttling:** Same pair won't spam console (5-second cooldown between identical alerts)
265
+ - **Idle Filtering:** Variables that haven't updated are excluded from analysis
202
266
 
203
- It's a **diagnostic tool** - it points out patterns worth investigating.
267
+ ### Action Required
268
+ None. Just update the package and restart your dev server.
269
+ ```bash
270
+ npm update react-state-basis
271
+ ```
204
272
 
205
273
  ---
206
274
 
207
275
  ## Production Safety
208
276
 
209
- In production builds, the entire tool is removed:
277
+ In production builds, the entire tool is replaced with zero-op shims. **Zero runtime overhead. Zero bundle size increase.**
210
278
  ```json
211
279
  // package.json - automatic based on NODE_ENV
212
280
  "exports": {
213
281
  ".": {
214
282
  "development": "./dist/index.mjs",
215
- "production": "./dist/production.mjs", // Zero-op shims
283
+ "production": "./dist/production.mjs", // No-op shims
216
284
  "default": "./dist/production.mjs"
217
285
  }
218
286
  }
219
287
  ```
220
288
 
221
- **Zero runtime overhead. Zero bundle size increase.**
289
+ ---
290
+
291
+ ## When to Skip Files
292
+
293
+ Add `// @basis-ignore` at the top of a file to disable instrumentation:
294
+ ```tsx
295
+ // @basis-ignore
296
+ // This file uses high-frequency animations and intentional state synchronization
297
+ ```
298
+
299
+ **Good candidates for skipping:**
300
+ - **High-frequency animations** (>60fps state updates)
301
+ - **WebSocket message handlers** (rapid, intentional updates)
302
+ - **Canvas/WebGL render loops** (performance-critical paths)
303
+ - **Intentional synchronization** (e.g., React Query → local cache mirrors)
304
+ - **Third-party library wrappers** (where you don't control the architecture)
222
305
 
223
306
  ---
224
307
 
225
308
  ## Comparison to Other Tools
226
309
 
227
- | Tool | What It Detects | When |
228
- |------|----------------|------|
229
- | **React DevTools** | Component renders, props/state values | After the fact |
230
- | **Why Did You Render** | Unnecessary re-renders | During render |
231
- | **ESLint exhaustive-deps** | Missing effect dependencies | Static analysis |
232
- | **react-state-basis** | Redundant state, update chains, loops | Runtime patterns |
310
+ | Tool | Focus | When to Use |
311
+ |------|-------|-------------|
312
+ | **React DevTools** | Component hierarchy & values | Debugging specific components |
313
+ | **Why Did You Render** | Re-render optimization | Performance tuning renders |
314
+ | **ESLint exhaustive-deps** | Static dependency analysis | Preventing missing deps |
315
+ | **react-state-basis** | **Temporal state relationships** | **Finding redundant state & effect chains** |
233
316
 
234
- They're complementary - use them together.
317
+ These tools are complementary - use them together for best results.
235
318
 
236
319
  ---
237
320
 
238
- ## Skipping Files
321
+ ## Performance Impact (Measured)
239
322
 
240
- Add this comment to skip noisy files:
241
- ```tsx
242
- // @basis-ignore
243
- ```
323
+ **Development Mode**
244
324
 
245
- Good for:
246
- - Animation loops
247
- - High-frequency timers
248
- - WebSocket handlers
249
- - Performance-critical code
325
+ These measurements were taken using the built-in **Stress Lab** with 100 active hooks and continuous state updates, observed in Chrome DevTools Performance and Web Vitals panels.
250
326
 
251
- ---
327
+ Location: `/example`
328
+
329
+ **Observed impact:**
252
330
 
253
- ## Limitations
331
+ * **Per state update overhead:** ~0.3ms (lightweight hook instrumentation)
332
+ * **Analysis pass (every ~100ms / 5 ticks):** ~2–4ms in typical applications
333
+ * **Frame budget impact:** <1% when idle, ~5–10% during active stress testing
334
+ * **Render path safety:** Analysis runs asynchronously, outside of React’s render cycle
254
335
 
255
- ### Current Version (v0.3.x)
336
+ > Results will vary by hardware, browser, and workload. Use the Stress Lab to reproduce and compare Basis ON vs OFF in your own environment.
337
+
338
+ <p align="center">
339
+ <img src="./assets/performance-test.png" width="800" alt="shadcn Admin Audit" />
340
+ </p>
341
+
342
+
343
+ **Production Mode:**
344
+ - Overhead: ~0.01ms per hook call (negligible wrapper overhead)
345
+ - Bundle size: ~2-3 KB minified (no-op wrappers only, no analysis engine)
346
+ - Monitoring logic: 100% removed
347
+ - HUD component: 100% removed
348
+
349
+ ---
350
+
351
+ ## Limitations (v0.4.0)
256
352
 
257
353
  **What works well:**
258
- - ✅ Detecting obvious redundant state
259
- - ✅ Flagging effect-driven update chains
260
- - ✅ Catching infinite loops
261
- - ✅ Works in typical React apps
354
+ - ✅ Detecting synchronous redundant state
355
+ - ✅ Flagging effect-driven update chains (A → Effect → B)
356
+ - ✅ Catching infinite loops before browser freeze
357
+ - ✅ Distinguishing causality from redundancy
262
358
 
263
- **Known issues:**
264
- - ⚠️ May miss delayed chains (>20ms apart)
265
- - ⚠️ Can flag intentional patterns as issues
266
- - ⚠️ Complex multi-way dependencies might not be caught
267
- - ⚠️ Requires judgment to interpret results
359
+ **Known edge cases:**
360
+ - ⚠️ **Async gaps:** Updates delayed by >40ms (e.g., slow API responses) may appear independent
361
+ - ⚠️ **Intentional sync:** Sometimes synchronization is required for library compatibility
362
+ - ⚠️ **Complex multi-way dependencies:** Three or more interconnected states might not show full relationship graph
363
+ - ⚠️ **Requires judgment:** Tool points out patterns worth investigating - you decide if they're issues
268
364
 
269
- **False positives happen.** Always verify before refactoring.
365
+ **False positives can happen.** Always verify before refactoring.
270
366
 
271
367
  ---
272
368
 
273
369
  ## Roadmap
274
370
 
275
- ### v0.4.0 (Next)
276
- - [ ] Zustand integration
277
- - [ ] Redux middleware
278
- - [ ] Better false positive filtering
279
- - [ ] Automated fix suggestions
371
+ ### v0.5.0 (Planned)
372
+ - [ ] Zustand & Redux middleware integration
373
+ - [ ] Visual dependency graph in HUD
374
+ - [ ] Automated fix suggestions with one-click apply
375
+ - [ ] Historical trend tracking across sessions
280
376
 
281
- ### v0.5.0 (Future)
282
- - [ ] Visual state dependency graph
283
- - [ ] Domain isolation analysis
284
- - [ ] Historical trend tracking
377
+ ### Future Ideas
378
+ - [ ] Domain isolation analysis (detect feature boundaries)
379
+ - [ ] Export audit reports for team reviews
380
+ - [ ] CI integration for architectural regressions
285
381
 
286
382
  ---
287
383
 
288
- ## Contributing
384
+ ## Troubleshooting
289
385
 
290
- Found a bug? Have an idea? Open an issue or PR.
386
+ **"I'm not seeing any alerts"**
387
+ 1. Verify `debug={true}` is set in `<BasisProvider>`
388
+ 2. Check that the Babel plugin is loaded (restart dev server after config changes)
389
+ 3. Create a test pattern (see "Verify It's Working" section)
390
+ 4. Open browser console and look for `Basis Auditor | Structural Relationship Check`
291
391
 
292
- For technical details on how the detection works, see the [Wiki](https://github.com/liovic/react-state-basis/wiki).
392
+ **"Too many alerts"**
393
+ - Use `// @basis-ignore` for animation-heavy files
394
+ - Check if your app has intentional state synchronization patterns
395
+ - Consider if alerts are revealing actual architectural issues
396
+
397
+ **"Alert says 'sync leak' but I need that effect"**
398
+ - Some effects are necessary (e.g., syncing to localStorage)
399
+ - Basis flags patterns, not bugs - use your judgment
400
+ - If it's intentional, add a comment explaining why
293
401
 
294
402
  ---
295
403
 
296
404
  ## FAQ
297
405
 
298
406
  **Q: Will this slow down my app?**
299
- A: Only in development. Production builds are zero-overhead.
407
+ A: Only in development (~0.3ms per update). Production builds have zero overhead.
300
408
 
301
409
  **Q: Do I have to change my code?**
302
410
  A: No. The Babel plugin instruments hooks automatically.
303
411
 
304
412
  **Q: What if it flags something that's not a problem?**
305
- A: Use your judgment. It's a diagnostic tool, not gospel.
413
+ A: Use your judgment. Basis is a diagnostic tool that points out patterns worth investigating - not all flagged patterns are bugs.
414
+
415
+ **Q: How is this different from Redux DevTools?**
416
+ A: Redux DevTools shows state values and action history. Basis shows temporal relationships between state variables, regardless of what state management library you use.
306
417
 
307
418
  **Q: Why "basis"?**
308
- A: In linear algebra, a "basis" is a minimal set of independent vectors. The name reflects the goal of finding your app's minimal independent state.
419
+ A: In linear algebra, a "basis" is a minimal set of independent vectors. The name reflects the goal of finding your app's minimal independent state - removing redundancy.
420
+
421
+ **Q: How is this different from Redux DevTools?**
422
+ A: Redux DevTools is a **Journal** - it logs specific values and actions within a Redux store. Basis is an **Architectural Auditor** - it instruments React's core primitives (useState, useReducer, useEffect) to detect hidden relationships between entirely separate components and hooks, even when they don't share a store. Redux DevTools answers "what changed and why?" while Basis answers "is this state architecture clean?"
423
+
424
+ ---
425
+
426
+ ## Contributing
427
+
428
+ Found a bug? Have an idea? Open an issue or PR.
429
+
430
+ For technical details on how the detection works, see the [Wiki](https://github.com/liovic/react-state-basis/wiki).
309
431
 
310
432
  ---
311
433