The simplest React state management library - direct mutations, direct destructuring, automatic fine-grained reactivity
npm install neantThe simplest React state management library - direct mutations, direct destructuring, automatic fine-grained reactivity.
The name "Neant" comes from French meaning "nothingness", symbolizing zero mental overhead state management.
Direct mutations, direct destructuring, automatic fine-grained reactivity - That's all the magic of Neant.
- Direct data mutation - No need to return new objects, just mutate directly
- Direct destructuring - Destructure states and actions directly from the hook
- Automatic fine-grained reactivity - Components only re-render when used states change
- Derived states with Hooks - Perfectly aligned with React philosophy, naturally intuitive
- Zero mental overhead - No complex concepts to learn, if you know React, you know Neant
- SSR support - Provider pattern seamlessly integrates server-side data
``bash`
npm install neant
`typescript
import { createStore } from 'neant';
const { useAppStore } = createStore((setState) => ({
// States
count: 0,
user: { name: 'John', age: 25 },
// Direct mutation, that simple
increment: () => setState(draft => {
draft.count += 1;
}),
decrement: () => setState(draft => {
draft.count -= 1;
}),
updateUser: (name: string) => setState(draft => {
draft.user.name = name;
}),
// Async is just as simple
fetchUser: async () => {
const response = await fetch('/api/user');
const userData = await response.json();
setState(draft => {
draft.user = userData;
});
},
}));
`
`tsx
import React from 'react';
function Counter() {
// Direct destructuring, automatic fine-grained subscription
const { count, increment, decrement } = useAppStore();
return (
Count: {count}
function UserProfile() {
// Only subscribe to needed states, other state changes won't affect this component
const { user, updateUser } = useAppStore();
return (
User: {user.name} ({user.age})
$3
#### Create Store Context
`typescript
// store/store-context.tsx
import { createContext, useContext, useRef, ReactNode } from 'react';
import { createStore } from 'neant';interface AppState {
count: number;
user: { name: string; age: number };
}
const createAppStore = (initialData?: Partial) => {
const { useAppStore, setState } = createStore((setState) => ({
count: 0,
user: { name: 'John', age: 25 },
increment: () => setState(draft => { draft.count += 1; }),
decrement: () => setState(draft => { draft.count -= 1; }),
updateUser: (name: string) => setState(draft => { draft.user.name = name; }),
}));
// Set initial data
if (initialData) {
setState(draft => {
Object.assign(draft, initialData);
});
}
return { useAppStore, setState };
};
const StoreContext = createContext | null>(null);
export const StoreProvider = ({
children,
initialData
}: {
children: ReactNode;
initialData?: Partial
}) => {
const storeRef = useRef | null>(null);
if (storeRef.current === null) {
storeRef.current = createAppStore(initialData);
}
return (
{children}
);
};
export const useAppStore = (): AppState => {
const storeContext = useContext(StoreContext);
if (!storeContext) {
throw new Error('useAppStore must be used within StoreProvider');
}
return storeContext.useAppStore();
};
`#### App Router
`typescript
// app/page.tsx
import { StoreProvider } from '../store/store-context';export default async function Page() {
const serverData = await fetchDataOnServer();
return (
);
}
`#### Page Router
`typescript
// pages/index.tsx
import { StoreProvider } from '../store/store-context';export default function Page({ serverData }) {
return (
);
}
export const getServerSideProps = async () => {
const serverData = await fetchDataOnServer();
return { props: { serverData } };
};
`Derived States - Use Hooks, Very React!
`typescript
// Just regular custom hooks, perfectly aligned with React philosophy
const useDouble = () => {
const { count } = useAppStore();
return count * 2;
};const useUserInfo = () => {
const { user } = useAppStore();
return {
displayName:
${user.name} (${user.age}),
isAdult: user.age >= 18
};
};function DerivedStateExample() {
const double = useDouble();
const { displayName, isAdult } = useUserInfo();
return (
Double count: {double}
User: {displayName}
Is adult: {isAdult ? 'Yes' : 'No'}
);
}
`Complex State Management
`typescript
const { useAppStore } = createStore((setState) => ({
todos: [],
filter: 'all',
loading: false,
// Direct array mutation, Immer handles immutability
addTodo: (text: string) => setState(draft => {
draft.todos.push({
id: Date.now(),
text,
completed: false,
});
}),
toggleTodo: (id: number) => setState(draft => {
const todo = draft.todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
}
}),
removeTodo: (id: number) => setState(draft => {
const index = draft.todos.findIndex(t => t.id === id);
if (index !== -1) {
draft.todos.splice(index, 1);
}
}),
// Async operations are also intuitive
fetchTodos: async () => {
setState(draft => { draft.loading = true; });
try {
const response = await fetch('/api/todos');
const todos = await response.json();
setState(draft => {
draft.todos = todos;
draft.loading = false;
});
} catch (error) {
setState(draft => { draft.loading = false; });
}
},
}));
`API Reference
$3
Create a new store.
Parameters:
-
stateCreator: (setState, getState) => initialStateReturns:
-
{ useAppStore, setState, getState, subscribe }$3
Update state, supports both sync and async.
Parameters:
-
updater: (draft) => void | async (draft) => voidExample Projects
Check the
examples/ directory:- Next.js App Router:
examples/nextjs-app-router/
- Next.js Page Router: examples/nextjs-page-router/`bash
App Router example
cd examples/nextjs-app-router
npm install
npm run devPage Router example
cd examples/nextjs-page-router
npm install
npm run dev
`$3
Add
'use client'; when using in App Router:`typescript
'use client';import { useAppStore } from './store';
``MIT License
Issues and Pull Requests are welcome!
---
Direct mutations, direct destructuring, zero mental overhead - That's Neant!