Nucleux: A Fresh Approach to State Management in React Applications

Marty RoqueMarty Roque
6 min read

State management remains one of the most discussed challenges in modern web development. As our applications grow in complexity, managing state across components becomes increasingly difficult. While there are numerous solutions on the market—Redux, Zustand, Recoil, Jotai—many developers still find themselves caught between excessive boilerplate and limited functionality.

Today, I'm excited to introduce Nucleux, a state management library that focuses on simplicity without sacrificing power.

The Evolution of State Management

The journey to better state management has been ongoing since React's early days. We've moved from prop drilling to Context API to more sophisticated solutions like Redux. Each iteration has aimed to solve specific problems:

  • Prop drilling became unwieldy as apps grew

  • Context API introduced performance challenges

  • Redux offered structure but at the cost of verbosity

  • Newer libraries simplified APIs but often lacked advanced features

Nucleux represents the next step in this evolution, building on what I learned from developing App's Digest (its predecessor) and incorporating feedback from real-world usage.

Introducing Nucleux

Nucleux is a state management library based on two powerful software patterns:

  1. Publisher-subscriber pattern - For efficient state updates

  2. Inversion of Control (IoC) container - For organized store management

But what makes it special is how these patterns translate into a developer-friendly experience:

// This simple code is all you need to create and use state
import { Store, useStore, useValue } from 'nucleux';

class CounterStore extends Store {
  count = this.atom(0);

  increment() {
    this.count.value += 1;
  }
}

function Counter() {
  const counterStore = useStore(CounterStore);
  const count = useValue(counterStore.count);

  return (
    <button onClick={() => counterStore.increment()}>
      Count: {count}
    </button>
  );
}

The code above showcases Nucleux's philosophy: state management shouldn't require excessive boilerplate or complex patterns.

Key Features That Make Nucleux Stand Out

1. Atomic Updates

Unlike global state solutions where updating one property can trigger unnecessary re-renders, Nucleux only updates components that are subscribed to specific atoms:

// Only components using counterStore.count will update
counterStore.count.value += 1;

// Other atoms remain untouched
counterStore.name.value = "New name";  // Independent update

2. No Provider Hell

We've all been there—wrapping our application in multiple nested providers:

// The old way with provider hell
<ThemeProvider>
  <AuthProvider>
    <CartProvider>
      <SettingsProvider>
        <App />
      </SettingsProvider>
    </CartProvider>
  </AuthProvider>
</ThemeProvider>

With Nucleux, this is completely eliminated. The IoC container manages store instances behind the scenes:

// The Nucleux way - no providers needed
<App />

3. Automatic Dependency Injection

Need one store to access another? Nucleux handles it elegantly:

class UserStore extends Store {
  currentUser = this.atom(null);
}

class CartStore extends Store {
  userStore = this.inject(UserStore);  // Inject the UserStore
  items = this.atom([]);

  checkout() {
    if (!this.userStore.currentUser.value) {
      throw new Error("User must be logged in");
    }
    // Process checkout...
  }
}

Building a Todo App with Nucleux

Let's see how these concepts translate to a real-world example by building a Todo app.

1. Create a Todo Store

// TodoStore.js
import { Store } from 'nucleux';
import { nanoid } from 'nanoid';

class TodoStore extends Store {
  // Create an atom to store todos with persistence
  todos = this.atom([], 'todos-storage');

  addTodo(text) {
    this.todos.value = [
      ...this.todos.value,
      {
        id: nanoid(),
        text,
        completed: false,
        createdAt: Date.now()
      }
    ];
  }

  toggleTodo(id) {
    this.todos.value = this.todos.value.map(todo => 
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    );
  }

  updateTodo(id, text) {
    this.todos.value = this.todos.value.map(todo => 
      todo.id === id ? { ...todo, text } : todo
    );
  }

  removeTodo(id) {
    this.todos.value = this.todos.value.filter(todo => todo.id !== id);
  }

  // Create a derived atom that sorts todos by creation date
  sortedTodos = this.deriveAtom(
    [this.todos],
    (todos) => {
      return [...todos].sort((a, b) => a.createdAt - b.createdAt);
    }
  );
}

export default TodoStore;

2. Create Todo Components

// TodoInput.jsx
import React, { useState } from 'react';
import { useStore } from 'nucleux';
import TodoStore from './TodoStore';

function TodoInput() {
  const [text, setText] = useState('');
  const todoStore = useStore(TodoStore);

  const handleSubmit = (e) => {
    e.preventDefault();
    if (text.trim()) {
      todoStore.addTodo(text);
      setText('');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="What needs to be done?"
      />
      <button type="submit">Add</button>
    </form>
  );
}

// TodoItem.jsx
import React from 'react';
import { useStore } from 'nucleux';
import TodoStore from './TodoStore';

function TodoItem({ id, text, completed }) {
  const todoStore = useStore(TodoStore);

  return (
    <div style={{ display: 'flex', alignItems: 'center', margin: '8px 0' }}>
      <input
        type="checkbox"
        checked={completed}
        onChange={() => todoStore.toggleTodo(id)}
      />
      <input
        value={text}
        onChange={(e) => todoStore.updateTodo(id, e.target.value)}
        style={{ 
          margin: '0 8px',
          textDecoration: completed ? 'line-through' : 'none' 
        }}
      />
      <button onClick={() => todoStore.removeTodo(id)}>Delete</button>
    </div>
  );
}

// TodoList.jsx
import React from 'react';
import { useStore, useValue } from 'nucleux';
import TodoStore from './TodoStore';
import TodoItem from './TodoItem';

function TodoList() {
  const todoStore = useStore(TodoStore);
  const todos = useValue(todoStore.sortedTodos);

  return (
    <div>
      {todos.map(todo => (
        <TodoItem key={todo.id} {...todo} />
      ))}
      <div style={{ marginTop: '16px' }}>
        {todos.length} item{todos.length !== 1 ? 's' : ''}
      </div>
    </div>
  );
}

// App.jsx
import React from 'react';
import TodoInput from './TodoInput';
import TodoList from './TodoList';

function App() {
  return (
    <div style={{ maxWidth: '500px', margin: '0 auto', padding: '20px' }}>
      <h1>Nucleux Todo App</h1>
      <TodoInput />
      <TodoList />
    </div>
  );
}

export default App;

3. Power Features in Action

Let's look at some of the advanced features Nucleux offers:

Built-in Persistence

Notice how we added persistence to our todos with just a string parameter:

todos = this.atom([], 'todos-storage');

This automatically saves todos to localStorage whenever they change and rehydrates them when the app loads.

Derived Atoms

Our sortedTodos derived atom automatically updates whenever the base todos atom changes:

sortedTodos = this.deriveAtom(
  [this.todos],
  (todos) => {
    return [...todos].sort((a, b) => a.createdAt - b.createdAt);
  }
);

This pattern keeps sorting logic out of your components and ensures consistent behavior throughout your application.

Performance Benefits

Nucleux's atomic update model provides significant performance benefits. Let's visualize what happens when state changes:

With traditional global state:

  1. State changes

  2. Every connected component re-renders

  3. Virtual DOM diffing helps, but unnecessary work still happens

With Nucleux:

  1. State atom changes

  2. Only components subscribed to that specific atom re-render

  3. Other components remain untouched

This approach scales exceptionally well for large applications with complex state dependencies.

Getting Started with Nucleux

Ready to try Nucleux in your project? Installation is straightforward:

# Using npm
npm install nucleux

# Using yarn
yarn add nucleux

# Using pnpm
pnpm add nucleux

Conclusion

State management doesn't have to be complicated. Nucleux proves that you can have both simplicity and power in a single library without compromise.

By combining the best aspects of publisher-subscriber patterns with IoC containers, Nucleux creates a developer experience that feels natural and intuitive. Whether you're building a small React application or a complex enterprise system, Nucleux scales with your needs without introducing unnecessary complexity.

Give Nucleux a try on your next project:

What state management challenges are you facing in your applications? Let me know in the comments, and I'd be happy to discuss how Nucleux might help solve them.

0
Subscribe to my newsletter

Read articles from Marty Roque directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Marty Roque
Marty Roque