DEV Community

Cover image for I built a runtime profiler that catches React state anti-patterns
Petar Liovic
Petar Liovic

Posted on • Edited on

I built a runtime profiler that catches React state anti-patterns

The Problem: State Gets Messy

In large React apps, we often end up with state that's harder to maintain than it needs to be. You've probably written code like this:

const [user, setUser] = useState(null);
const [isLoggedIn, setIsLoggedIn] = useState(false);

const onLogin = (data) => {
   setUser(data);
   setIsLoggedIn(true); // Do we need both?
};
Enter fullscreen mode Exit fullscreen mode

isLoggedIn isn't really independent—it's just !!user. But keeping them in sync manually creates extra work and potential bugs.

Or this pattern:

const [celsius, setCelsius] = useState(0);
const [fahrenheit, setFahrenheit] = useState(32);

useEffect(() => {
  setFahrenheit(celsius * 9/5 + 32); // Double render!
}, [celsius]);
Enter fullscreen mode Exit fullscreen mode

This causes two renders every time: one for celsius, then another for fahrenheit. A useMemo would be better.

These aren't syntax errors. They work. But they create architectural debt that compounds over time.

The Challenge: How Do You Find These?

Standard tools like React DevTools show you what your state is right now. ESLint catches some hook mistakes. But nothing really tells you:

  • "These two states always update together—they're probably redundant"
  • "This useEffect is creating a double-render cycle"
  • "You have an infinite loop about to freeze your browser"

So I built something that does.

The Tool: react-state-basis

It's a development-time profiler that watches when state updates happen (not the values themselves) and detects patterns that suggest architectural problems.

What it catches:

1. Redundant State
When two states always update together, it suggests refactoring to a single source of truth or useMemo.

2. Double-Render Cycles
When a useEffect triggers another state update, causing an extra render.

3. Infinite Loops
High-frequency oscillations that would normally freeze your browser—caught and stopped before they lock up your tab.

4. Hidden Coupling
States that should be independent but are actually synchronized (even across different contexts).

How it looks

Detecting Redundant Boolean Flags

Booleans Example

The Problem: Using multiple boolean flags (isLoading, isSuccess, hasData) often leads to "impossible states."

What Basis catches: Even though these are separate variables, they update in perfect sync. It flags them as redundant and suggests consolidating into a single state machine or status enum.


Circuit Breaker for Infinite Loops

Circuit Breaker

The Problem: A recursive useEffect causing an infinite loop—normally this freezes your browser.

What Basis does: Detects high-frequency oscillations (25+ updates in 500ms) and automatically halts the update chain before it locks up. Gives you a diagnostic report showing where the loop is.


Catching Manual State Synchronization

Manually Syncing

The Problem: Manually syncing fahrenheit via useEffect creates a double-render cycle.

What Basis suggests: It identifies the sequential dependency and recommends refactoring to useMemo for a cleaner, single-render solution.


Finding Hidden Cross-Context Dependencies

Cross Context

The Scenario: You have separate contexts (AuthContext, ThemeContext), but they're manually synchronized in your logic (e.g., switching to dark theme when a user logs in).

What Basis reveals: By analyzing update timing across your entire app, it identifies that user and theme are moving in perfect sync—exposing hidden coupling between supposedly independent parts of your architecture.


System Health Check

Health Check

Architectural Overview: Basis performs a global audit showing how much of your state is actually independent vs. redundant.

An efficiency of 40% means 60% of your state variables are synchronized with others—they could probably be derived or consolidated.

The Goal: Get closer to 100% efficiency, where every state variable serves as a true independent source of truth.


How It Works (High Level)

The tool runs only in development and tracks the timing of state updates across a sliding window. By looking at when updates happen (not what the values are), it can detect:

  • States that always change together
  • Sequential update chains
  • Oscillating patterns

It's inspired by signal processing and correlation analysis—think of it like a profiler for your state architecture.

Zero Production Overhead

The detection engine completely disappears in production builds. Your production bundle stays clean—this only runs during development.

Try it out

I just released this as open source. Looking for feedback from engineers who've dealt with messy state architecture.

GitHub: https://github.com/liovic/react-state-basis

NPM: npm i react-state-basis

Setup is straightforward—uses a Babel plugin to automatically instrument your hooks. No need to change your component code.


Why I Built This

After working on large React codebases for years, I kept seeing the same patterns cause problems:

  • Boolean flags that should be enums
  • Manual synchronization that should be derivation
  • Hidden dependencies across contexts
  • Infinite loops that only appear in production

Most of these aren't caught by linters or type checkers. They're architectural issues that emerge over time. I wanted a tool that could spot them automatically.


If you've struggled with state management complexity, I'd love to hear your thoughts. What patterns do you wish tools could catch automatically?

Star the repo if this looks useful, or open an issue with feedback. Always looking to improve it.


Keep your state minimal and your architecture clean.

Top comments (0)