DEV Community

reactuse.com
reactuse.com

Posted on

Why I Built Neant: A React State Management Library with Zero Mental Overhead

πŸš€ Discover 100+ powerful React Hooks possibilities! Visit www.reactuse.com for comprehensive documentation with MCP (Model Context Protocol) support, or install via npm install @reactuses/core to supercharge your React development efficiency with our extensive hook collection!

Prologue

I've been frustrated with existing React state management solutions for a while. They all seem to require learning too many concepts just to update some state. So I decided to build something different: Neant.

What I Wanted to Achieve

When designing Neant, I had a clear vision of what state management should feel like:

Just Mutate Things Directly

import { createStore } from 'neant';

const { useAppStore } = createStore((setState) => ({
  count: 0,
  user: { name: 'John', age: 25 },

  // Want to change something? Just change it.
  increment: () => setState(draft => {
    draft.count += 1;
  }),

  updateUser: (name) => setState(draft => {
    draft.user.name = name;
  }),
}));
Enter fullscreen mode Exit fullscreen mode

No thinking about immutability. No spread operators everywhere. Just focus on your business logic.

Performance Should Be Automatic

function UserProfile() {
  const { user } = useAppStore();  // Only re-renders when user changes
  return <div>{user.name}</div>;
}

function Counter() {
  const { count } = useAppStore();  // Only re-renders when count changes
  return <div>{count}</div>;
}
Enter fullscreen mode Exit fullscreen mode

I wanted components to automatically subscribe to only the state they actually use. No manual optimization needed.

Derived State Should Just Be React

const useDisplayName = () => {
  const { user } = useAppStore();
  return `${user.name} (age ${user.age})`;
};

const useIsAdult = () => {
  const { user } = useAppStore();
  return user.age >= 18;
};

function UserInfo() {
  const displayName = useDisplayName();
  const isAdult = useIsAdult();

  return (
    <div>
      <p>{displayName}</p>
      <p>{isAdult ? 'Adult' : 'Minor'}</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Why invent new concepts? React hooks already solve derived state perfectly.

Getting Started in 5 Minutes

Here's everything you need to build a working app:

npm install neant
Enter fullscreen mode Exit fullscreen mode
// store.js
import { createStore } from 'neant';

export const { useAppStore } = createStore((setState) => ({
  // State
  todos: [],

  // Actions
  addTodo: (text) => setState(draft => {
    draft.todos.push({
      id: Date.now(),
      text,
      completed: false,
    });
  }),

  toggleTodo: (id) => setState(draft => {
    const todo = draft.todos.find(t => t.id === id);
    if (todo) {
      todo.completed = !todo.completed;
    }
  }),
}));
Enter fullscreen mode Exit fullscreen mode
// App.js
import { useAppStore } from './store';

function App() {
  const { todos, addTodo, toggleTodo } = useAppStore();

  return (
    <div>
      <button onClick={() => addTodo('New task')}>
        Add Task
      </button>

      {todos.map(todo => (
        <div key={todo.id}>
          <span 
            style={{ 
              textDecoration: todo.completed ? 'line-through' : 'none' 
            }}
            onClick={() => toggleTodo(todo.id)}
          >
            {todo.text}
          </span>
        </div>
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

That's it. A complete todo app.

Async is Just Async

const { useAppStore } = createStore((setState) => ({
  posts: [],
  loading: false,

  fetchPosts: async () => {
    setState(draft => { draft.loading = true; });

    try {
      const response = await fetch('/api/posts');
      const posts = await response.json();

      setState(draft => {
        draft.posts = posts;
        draft.loading = false;
      });
    } catch (error) {
      setState(draft => { draft.loading = false; });
    }
  },
}));
Enter fullscreen mode Exit fullscreen mode

No special middleware. No new syntax to learn. Async operations work exactly like you'd expect.

Next.js Integration

I made sure SSR works smoothly:

// store-context.tsx
export const StoreProvider = ({ children, initialData }) => {
  const storeRef = useRef(null);

  if (storeRef.current === null) {
    storeRef.current = createAppStore(initialData);
  }

  return (
    <StoreContext.Provider value={storeRef.current}>
      {children}
    </StoreContext.Provider>
  );
};

// page.tsx
export default async function Page() {
  const serverData = await fetchServerData();

  return (
    <StoreProvider initialData={serverData}>
      <MyApp />
    </StoreProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Server data flows seamlessly into the client store.

Who Should Use Neant?

Based on my design goals, Neant works great for:

  • Fast development: When you don't want to spend time learning complex concepts
  • Small to medium apps: When you don't need time-travel debugging
  • Mixed-skill teams: New developers can pick it up quickly
  • Developer experience focus: When you want state management to feel like regular JavaScript

My Philosophy

While building Neant, I kept asking myself: what's the essence of state management?

I believe it's about letting developers focus on business logic, not getting distracted by concepts and patterns. The best tools should be "invisible" β€” you don't notice them, but they help you get things done.

That's why I named it "Neant" (French for "nothingness"). I want developers to forget they're using a state management library and just focus on writing React.

The goal isn't to be the most feature-rich or have the most sophisticated API. It's to get out of your way and let you build.

If this resonates with you, give Neant a try. I'm continuing to refine it to make React development feel more natural.


Check it out: GitHub

Let me know what you think or if you have any suggestions!

Top comments (0)