UNPKG

neant

Version:

The simplest React state management library - direct mutations, direct destructuring, automatic fine-grained reactivity

336 lines (257 loc) 7.46 kB
# Neant *[中文文档](README-zh.md)* 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 ```bash npm install neant ``` ## Quick Start ### Create Store ```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; }); }, })); ``` ### Use in Components ```tsx 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 ```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<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 ```typescript // 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 ```typescript // 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! ```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 ( <div> <p>Double count: {double}</p> <p>User: {displayName}</p> <p>Is adult: {isAdult ? 'Yes' : 'No'}</p> </div> ); } ``` ## 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 ### `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/` ```bash # 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 dev ``` ### Next.js Client Components Add `'use client';` when using in App Router: ```typescript '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!**