@naamanu/speck
v0.1.0
Published
A lightweight state management library for React
Maintainers
Readme
Speck
A lightweight state management library for React, inspired by Recoil and Jotai.
Features
- Atomic state management: Define independent pieces of state as atoms
- Derived state: Create selectors that compute values from atoms
- Minimal boilerplate: Simple API with React hooks
- TypeScript support: Full type safety out of the box
- Lightweight: Small bundle size with no external dependencies
Installation
npm install @naamanu/speckQuick Start
1. Wrap your app with the Provider
import { Provider } from '@naamanu/speck';
function App() {
return (
<Provider>
<YourApp />
</Provider>
);
}2. Create atoms
import { atom } from '@naamanu/speck';
const countAtom = atom(0);
const nameAtom = atom('John');3. Use atoms in your components
import { useAtom, useAtomValue, useSetAtom } from '@naamanu/speck';
function Counter() {
// Read and write
const [count, setCount] = useAtom(countAtom);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
function DisplayCount() {
// Read only
const count = useAtomValue(countAtom);
return <p>Current count: {count}</p>;
}
function IncrementButton() {
// Write only
const setCount = useSetAtom(countAtom);
return <button onClick={() => setCount(c => c + 1)}>Increment</button>;
}4. Create selectors for derived state
import { atom, selector, useAtomValue } from '@naamanu/speck';
const numberAtom = atom(5);
const doubledSelector = selector((get) => {
const number = get(numberAtom);
return number * 2;
});
function DoubledValue() {
const doubled = useAtomValue(doubledSelector);
return <p>Doubled: {doubled}</p>;
}API Reference
atom<T>(defaultValue: T): Atom<T>
Creates an atom with the given default value.
const userAtom = atom({ name: 'Alice', age: 30 });selector<T>(getter: (get) => T): ReadOnlyAtom<T>
Creates a read-only selector that derives its value from other atoms or selectors.
const fullNameSelector = selector((get) => {
const firstName = get(firstNameAtom);
const lastName = get(lastNameAtom);
return `${firstName} ${lastName}`;
});useAtom<T>(atom: Atom<T>): [T, (value: T) => void]
Hook that returns both the current value and a setter function.
const [count, setCount] = useAtom(countAtom);useAtomValue<T>(atom: Atom<T> | ReadOnlyAtom<T>): T
Hook that returns the current value (read-only).
const count = useAtomValue(countAtom);
const doubled = useAtomValue(doubledSelector);useSetAtom<T>(atom: Atom<T>): (value: T) => void
Hook that returns only the setter function.
const setCount = useSetAtom(countAtom);Provider
Context provider that must wrap your app to enable atom functionality.
<Provider>
<App />
</Provider>Example
import { atom, selector, Provider, useAtom, useAtomValue } from '@naamanu/speck';
// Define atoms
const todosAtom = atom([]);
const filterAtom = atom('all');
// Define selectors
const filteredTodosSelector = selector((get) => {
const todos = get(todosAtom);
const filter = get(filterAtom);
if (filter === 'completed') {
return todos.filter(todo => todo.completed);
}
if (filter === 'active') {
return todos.filter(todo => !todo.completed);
}
return todos;
});
function TodoApp() {
return (
<Provider>
<TodoList />
<FilterButtons />
</Provider>
);
}
function TodoList() {
const todos = useAtomValue(filteredTodosSelector);
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
function FilterButtons() {
const [filter, setFilter] = useAtom(filterAtom);
return (
<div>
<button onClick={() => setFilter('all')}>All</button>
<button onClick={() => setFilter('active')}>Active</button>
<button onClick={() => setFilter('completed')}>Completed</button>
</div>
);
}Development
# Install dependencies
npm install
# Run demo app
npm run dev
# Build library
npm run build
# Lint
npm run lintLicense
MIT
