npm package: @naamanu/speck
Description: A lightweight state management library for React
Author: Nana Adjei Manu
Latest version: 0.1.0

# 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

```bash
npm install @naamanu/speck
```

## Quick Start

### 1. Wrap your app with the Provider

```tsx
import { Provider } from '@naamanu/speck';

function App() {
  return (
    <Provider>
      <YourApp />
    </Provider>
  );
}
```

### 2. Create atoms

```tsx
import { atom } from '@naamanu/speck';

const countAtom = atom(0);
const nameAtom = atom('John');
```

### 3. Use atoms in your components

```tsx
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

```tsx
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.

```tsx
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.

```tsx
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.

```tsx
const [count, setCount] = useAtom(countAtom);
```

### `useAtomValue<T>(atom: Atom<T> | ReadOnlyAtom<T>): T`

Hook that returns the current value (read-only).

```tsx
const count = useAtomValue(countAtom);
const doubled = useAtomValue(doubledSelector);
```

### `useSetAtom<T>(atom: Atom<T>): (value: T) => void`

Hook that returns only the setter function.

```tsx
const setCount = useSetAtom(countAtom);
```

### `Provider`

Context provider that must wrap your app to enable atom functionality.

```tsx
<Provider>
  <App />
</Provider>
```

## Example

```tsx
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

```bash
# Install dependencies
npm install

# Run demo app
npm run dev

# Build library
npm run build

# Lint
npm run lint
```

## License

MIT