-
Notifications
You must be signed in to change notification settings - Fork 3
The Forensic Catalog
Basis uses Graph Theory and Signal Processing to find architectural debt. Unlike linters, it doesn't look at code syntax. It looks at the topology of state updates in real-time.
Below are the patterns Basis detects, ranked by severity.
Signal: A single external trigger (click, timer, fetch) causes simultaneous updates to multiple independent state roots (
Why it matters: The application state is fragmented. Because these updates hit different roots (often different Contexts or files), React may tear the UI or trigger waterfalls.
// Inside a component or event handler
const handleLogin = () => {
setUser(userData); // Root 1 (AuthContext)
setTheme('dark'); // Root 2 (ThemeContext)
setLastLogin(Date.now); // Root 3 (UserMetaContext)
}Consolidate these variables into a single Store or Reducer so the transition happens as one atomic update.
🎯 REFACTOR PRIORITIES (PRIME MOVERS)
1 ⚡ Global Event (handleLogin) (AuthContext.tsx)
Global Sync Event: An external trigger is updating 3 roots simultaneously. Occurred 12 times.
Impacts:AuthContext.tsx(user) +ThemeContext.tsx(theme) +UserMeta.tsx(lastLogin)
Solution: These variables update together but live in different hooks/files. Consolidate them into a singleuseReduceror atomic store update.
Signal: State A updates, triggers an Effect, which immediately updates State B in the next frame (
Why it matters: You're forcing the browser to paint, run logic, then immediately repaint. This causes layout shifts and wastes CPU cycles.
const [data, setData] = useState(null);
const [isValid, setIsValid] = useState(false);
useEffect(() => {
setIsValid(check(data)); // ⚡ Triggers a second render
}, [data]);const [data, setData] = useState(null);
// Calculate during render phase (zero cost)
const isValid = check(data); ⚡ BASIS | DOUBLE RENDER
📍 Location:ValidationForm.tsx
Issue:effect_L45triggersisValidin a separate frame.
Fix: DeriveisValidduring the render phase (remove effect) or wrap inuseMemo.
Signal: Two local variables update in perfect unison (
Why it matters: You're storing the same information twice. This breaks the "Single Source of Truth" principle.
const [firstName, setFirst] = useState('John');
const [fullName, setFull] = useState('John Doe'); // Redundantconst [firstName, setFirst] = useState('John');
const fullName = `${firstName} Doe`;♊ BASIS | DUPLICATE STATE
📍 Location:UserProfile.tsx
Issue:firstNameandfullNameare synchronized (100%).
Fix: Redundant State detected. DerivefullNamefromfirstNameduring render, or useuseMemo.
Signal: A subtype of Duplicate State where 3+ boolean flags toggle in a synchronized pattern.
Why it matters: You've built an implicit state machine with "impossible states" (e.g., isLoading: true AND isSuccess: true at the same time).
const [isLoading, setLoading] = useState(false);
const [isSuccess, setSuccess] = useState(false);
const [hasError, setError] = useState(false);// One variable, zero impossible states
const [status, setStatus] = useState('idle' | 'loading' | 'success' | 'error');♊ BASIS | DUPLICATE STATE
Fix: Boolean Explosion detected. Merge flags into a singlestatusstring oruseReducer.
Signal: A local state variable perfectly mirrors a Context value.
Why it matters: You've created a local copy of a global truth. If the context updates and your local state doesn't catch it (or the other way around), you get bugs. This is state drift.
const { user } = useContext(AuthContext);
const [localName, setLocalName] = useState(user.name); // 🚩 Shadow copy
useEffect(() => {
setLocalName(user.name); // Manual sync
}, [user]);Use the context value directly in your JSX. Delete the local copy.
♊ BASIS | CONTEXT MIRRORING
Issue:localNameandAuthContextare synchronized (100%).
Fix: Local state is 'shadowing' Global Context. Delete the local state and consume the Context value directly.
Signal: A state variable updates >150 times per second.
Why it matters: This is a critical logic error. Most likely a useEffect that updates its own dependency. Basis kills the update to save your browser tab.
🛑 BASIS CRITICAL | CIRCUIT BREAKER
INFINITE LOOP DETECTED
Variable:count| Frequency:151 updates/sec.
ACTION: Update BLOCKED to prevent browser freeze.
You'll see two types of output from Basis. When they seem to contradict each other, here's how to think about it.
-
What it does: Flags issues the moment they happen. Works with local pattern matching (
$A \leftrightarrow B$ ). - Limitation: It assumes variables are local. It doesn't know about file boundaries.
-
Example: It sees
userandthemechanging together. It suggests: "Merge these via useMemo."
-
What it does: Analyzes the entire causal chain after the fact. Works with the full graph topology (
$Event \rightarrow \lbrace A, B \rbrace$ ). - Advantage: It knows file paths and context boundaries.
-
Example: It sees
user(inAuthContext.tsx) andtheme(inThemeContext.tsx) triggered by the same event. It knowsuseMemois impossible across files. It overrules the Stream and suggests: "Consolidate into a Global Store/Reducer."
If the Stream and the Report disagree, trust the Report. The Report has the full topological context that the real-time stream doesn't have. The Stream finds the symptom. The Report finds the root cause.