neant
v1.0.0
Published
The simplest React state management library - direct mutations, direct destructuring, automatic fine-grained reactivity
Maintainers
Readme
Neant
The 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.
Why Choose Neant?
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
Installation
npm install neantQuick Start
Create Store
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;
});
},
}));Use in Components
import React from 'react';
function Counter() {
// Direct destructuring, automatic fine-grained subscription
const { count, increment, decrement } = useAppStore();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}
function UserProfile() {
// Only subscribe to needed states, other state changes won't affect this component
const { user, updateUser } = useAppStore();
return (
<div>
<p>User: {user.name} ({user.age})</p>
<button onClick={() => updateUser('Jane')}>
Change Name
</button>
</div>
);
}Next.js SSR Integration
Create Store Context
// 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<AppState>) => {
const { useAppStore, setState } = createStore<AppState>((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<ReturnType<typeof createAppStore> | null>(null);
export const StoreProvider = ({
children,
initialData
}: {
children: ReactNode;
initialData?: Partial<AppState>
}) => {
const storeRef = useRef<ReturnType<typeof createAppStore> | null>(null);
if (storeRef.current === null) {
storeRef.current = createAppStore(initialData);
}
return (
<StoreContext.Provider value={storeRef.current}>
{children}
</StoreContext.Provider>
);
};
export const useAppStore = (): AppState => {
const storeContext = useContext(StoreContext);
if (!storeContext) {
throw new Error('useAppStore must be used within StoreProvider');
}
return storeContext.useAppStore();
};App Router
// app/page.tsx
import { StoreProvider } from '../store/store-context';
export default async function Page() {
const serverData = await fetchDataOnServer();
return (
<StoreProvider initialData={serverData}>
<YourComponents />
</StoreProvider>
);
}Page Router
// pages/index.tsx
import { StoreProvider } from '../store/store-context';
export default function Page({ serverData }) {
return (
<StoreProvider initialData={serverData}>
<YourComponents />
</StoreProvider>
);
}
export const getServerSideProps = async () => {
const serverData = await fetchDataOnServer();
return { props: { serverData } };
};Derived States - Use Hooks, Very React!
// 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 (
<div>
<p>Double count: {double}</p>
<p>User: {displayName}</p>
<p>Is adult: {isAdult ? 'Yes' : 'No'}</p>
</div>
);
}Complex State Management
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
createStore(stateCreator)
Create a new store.
Parameters:
stateCreator:(setState, getState) => initialState
Returns:
{ useAppStore, setState, getState, subscribe }
setState(updater)
Update state, supports both sync and async.
Parameters:
updater:(draft) => void | async (draft) => void
Example Projects
Check the examples/ directory:
- Next.js App Router:
examples/nextjs-app-router/ - Next.js Page Router:
examples/nextjs-page-router/
# App Router example
cd examples/nextjs-app-router
npm install
npm run dev
# Page Router example
cd examples/nextjs-page-router
npm install
npm run devNext.js Client Components
Add 'use client'; when using in App Router:
'use client';
import { useAppStore } from './store';License
MIT License
Contributing
Issues and Pull Requests are welcome!
Direct mutations, direct destructuring, zero mental overhead - That's Neant!
