DEV Community

Marty Roque
Marty Roque

Posted on • Originally published at Medium

How Layered Memoization in Nucleux v1.3.0 Eliminates React's Biggest Performance Pitfall

React applications slow down for one primary reason: unnecessary re-renders. You update a user's name, and suddenly your entire shopping cart re-renders. You toggle a modal, and your complex data table rebuilds itself. Sound familiar?

This cascade effect has plagued React developers for years, leading to complex optimization strategies with React.memo, useMemo, and useCallback. But what if your state management library could eliminate this problem entirely?

The Hidden Cost of State Updates

Consider this common scenario in a typical React app:

// Traditional approach - everything re-renders
function App() {
  const [user, setUser] = useState({ name: 'John', notifications: 0 })
  const [cart, setCart] = useState([])
  const [theme, setTheme] = useState('dark')

  // Changing user.name triggers re-renders in:
  // - UserProfile component
  // - Cart component (unnecessary!)
  // - ThemeToggle component (unnecessary!)

  return (
    <div>
      <UserProfile user={user} />
      <ShoppingCart cart={cart} />
      <ThemeToggle theme={theme} />
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

When user.name changes, React re-renders every component that receives any piece of this state, even if they don't use the changed data. This becomes exponentially worse as your app grows.

Atomic Updates: The First Step

Nucleux solves this with atomic state management - only components subscribed to specific atoms re-render:

class AppStore extends Store {
  user = this.atom({ name: 'John', notifications: 0 })
  cart = this.atom([])
  theme = this.atom('dark')

  updateUserName(name) {
    this.user.value = { ...this.user.value, name }
  }
}

function UserProfile() {
  const user = useValue(AppStore, 'user')
  // Only this component re-renders when user changes
}

function ShoppingCart() {
  const cart = useValue(AppStore, 'cart')
  // This NEVER re-renders when user changes
}
Enter fullscreen mode Exit fullscreen mode

But even atomic updates have a subtle performance issue...

The Memoization Problem

What happens when you update a user object with the same values?

// This triggers a re-render even though nothing actually changed!
store.updateUserName('John') // Same name as before
Enter fullscreen mode Exit fullscreen mode

React compares objects by reference, not content. Even if the data is identical, creating a new object triggers unnecessary updates.

Enter Layered Memoization

Nucleux v1.3.0 introduces layered memoization that intelligently prevents updates when values haven't actually changed:

Shallow Memoization (Default)

Perfect for primitive values and objects you replace entirely:

class CounterStore extends Store {
  count = this.atom(0) // Automatically uses shallow memoization

  increment() {
    this.count.value += 1 // Only updates when value actually changes
  }
}
Enter fullscreen mode Exit fullscreen mode

Deep Memoization

Ideal for complex objects where you care about content, not reference:

class UserStore extends Store {
  profile = this.atom(
    { name: 'John', email: '[email protected]', preferences: {...} },
    { memoization: { type: 'deep' } }
  )

  updateProfile(newProfile) {
    // Only re-renders if the content actually changed
    this.profile.value = newProfile
  }
}
Enter fullscreen mode Exit fullscreen mode

Custom Memoization

For specialized comparison logic:

class ProductStore extends Store {
  product = this.atom(
    { id: 1, name: 'Laptop', price: 999.99, lastUpdated: Date.now() },
    {
      memoization: {
        type: 'custom',
        compare: (prev, next) => 
          prev.id === next.id && 
          prev.name === next.name && 
          prev.price === next.price
        // Ignores lastUpdated timestamp
      }
    }
  )
}
Enter fullscreen mode Exit fullscreen mode

Real-World Performance Impact

Here's a before/after comparison in a typical e-commerce app:

Without Memoization:

  • User updates profile → 12 components re-render
  • Product price change → 8 components re-render
  • Cart item quantity update → 15 components re-render

With Layered Memoization:

  • User updates profile with same data → 0 components re-render
  • Product price change with same price → 0 components re-render
  • Cart update with identical items → 0 components re-render

Beyond Atoms: Memoized Derived State

Derived state benefits from memoization too:

class TodoStore extends Store {
  todos = this.atom([])
  filter = this.atom('all')

  // Won't recalculate if filtered result contains same items
  filteredTodos = this.deriveAtom(
    [this.todos, this.filter],
    (todos, filter) => todos.filter(t => t.status === filter),
    { type: 'deep' } // Compares array content, not reference
  )
}
Enter fullscreen mode Exit fullscreen mode

The Bottom Line

Layered memoization transforms your React app's performance by:

  1. Eliminating phantom updates - No re-renders when data hasn't actually changed
  2. Optimizing derived state - Complex calculations only run when inputs truly differ
  3. Reducing debugging complexity - Fewer unexpected re-renders to track down
  4. Improving user experience - Smoother interactions and better responsiveness

The best part? It works automatically. Set your memoization strategy once, and Nucleux handles the intelligent comparisons behind the scenes.

Try It Yourself

Want to experience the performance difference? Check out the complete documentation to see how layered memoization can transform your React application's performance.


Nucleux v1.3.0 is available now on npm. Upgrade today and feel the difference intelligent memoization makes in your React applications.

Top comments (0)

OSZAR »