npm package: @okenneth/reactive-state
Description: A lightweight, reactive state management library for React with TypeScript support
Author: Owusu Kenneth
Latest version: 1.0.1

# ReactiveState

A lightweight, reactive state management library for React with TypeScript support.

## Features

- **Type Safety**: Fully typed API using TypeScript.
- **Lightweight**: Minimal bundle size, avoiding unnecessary dependencies.
- **Intuitive API**: Simple, hook-based API that feels native to React.
- **Fine-Grained Reactivity**: Components only re-render when the specific part of the state they depend on changes.
- **Flexible**: Support for both atomic and global store patterns.
- **Optimized Performance**: Efficient state updates, avoiding unnecessary renders.
- **Persistable State**: Support for localStorage or custom storage solutions.
- **Asynchronous Actions**: Built-in support for async operations.
- **Selectors & Derivations**: Computed state and optimized subscriptions.
- **Middleware Support**: Logging, async processing, validation, and more.
- **SSR Compatible**: Works with server-side rendering frameworks like Next.js.

## Installation

```bash
npm install @okenneth/reactive-state
# or
yarn add @okenneth/reactive-state
# or
pnpm add @okenneth/reactive-state
```

## Basic Usage

### Creating a Store

```tsx
import { createStore } from '@okenneth/reactive-state';

// Define your state type
interface CounterState {
  count: number;
  incrementBy: number;
}

// Create a store with initial state
const counterStore = createStore<CounterState>({
  initialState: {
    count: 0,
    incrementBy: 1
  }
});

// Use the store directly
counterStore.setState(state => ({
  ...state,
  count: state.count + state.incrementBy
}));

console.log(counterStore.getState()); // { count: 1, incrementBy: 1 }
```

### Using Stores in Components

```tsx
import React from 'react';
import { useStore, useSelector } from '@okenneth/reactive-state';

function Counter() {
  // Subscribe to the entire store
  const state = useStore(counterStore);
  
  const increment = () => {
    counterStore.setState(state => ({
      ...state,
      count: state.count + state.incrementBy
    }), 'INCREMENT');
  };
  
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={increment}>Increment by {state.incrementBy}</button>
    </div>
  );
}

function CountDisplay() {
  // Subscribe to only the count part of the state
  const count = useSelector(
    counterStore,
    state => state.count
  );
  
  // This component only re-renders when count changes
  return <p>Current count: {count}</p>;
}
```

### Using the Combined State and Setter Hook

```tsx
import React from 'react';
import { useStoreState } from '@okenneth/reactive-state';

function CounterWithHook() {
  // Get both state and setState in one hook
  const [state, setState] = useStoreState(counterStore);
  
  const increment = () => {
    setState(state => ({
      ...state,
      count: state.count + state.incrementBy
    }), 'INCREMENT');
  };
  
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={increment}>Increment by {state.incrementBy}</button>
    </div>
  );
}
```

### Async Actions

```tsx
import { AsyncAction } from '@okenneth/reactive-state';

// Define an async action
const fetchUserData: AsyncAction<UserState> = async (
  state,
  setState,
  getState
) => {
  // Show loading state
  setState(state => ({ ...state, loading: true }), 'FETCH_USER_START');
  
  try {
    // Fetch data from API
    const response = await fetch('/api/user');
    const userData = await response.json();
    
    // Update state with the result
    setState(state => ({
      ...state,
      user: userData,
      loading: false
    }), 'FETCH_USER_SUCCESS');
    
    return userData;
  } catch (error) {
    // Handle error
    setState(state => ({
      ...state,
      error: error.message,
      loading: false
    }), 'FETCH_USER_ERROR');
    
    throw error;
  }
};

// Dispatch the action
userStore.dispatch(fetchUserData).then(user => {
  console.log('User data loaded:', user);
}).catch(error => {
  console.error('Failed to load user data:', error);
});
```

### Creating Derived State

```tsx
import { createDerivedStore } from '@okenneth/reactive-state';

// Create a derived store that depends on another store
const doubledCountStore = createDerivedStore(
  [counterStore],
  (counterState) => ({
    doubledCount: counterState.count * 2,
    isEven: (counterState.count * 2) % 2 === 0
  })
);

// Use in a component with useStore hook
function DoubledCounter() {
  const { doubledCount, isEven } = useStore(doubledCountStore);
  
  return (
    <div>
      <p>Doubled count: {doubledCount}</p>
      <p>Is even: {isEven ? 'Yes' : 'No'}</p>
    </div>
  );
}
```

### Using Middleware

```tsx
import {
  createStore,
  createLoggerMiddleware,
  createPerformanceMiddleware
} from '@okenneth/reactive-state';

// Create a store with middleware
const storeWithMiddleware = createStore({
  initialState: { count: 0 },
  middleware: [
    createLoggerMiddleware({ name: 'CounterStore' }),
    createPerformanceMiddleware({ warnIfExceeds: 5 })
  ]
});
```

### Persistable State

```tsx
import { createStore } from '@okenneth/reactive-state';

// Create a store with persistence enabled
const persistedStore = createStore({
  initialState: { preferences: { theme: 'light' } },
  persist: {
    key: 'app-preferences',
    storage: localStorage // Optional, defaults to localStorage
  }
});
```

### Undo/Redo Functionality

```tsx
import React from 'react';
import { createStore, createUndoRedoMiddleware } from '@okenneth/reactive-state';

const initialState = { text: '' };

// Create the undo/redo middleware
const { middleware, controls } = createUndoRedoMiddleware();

// Create a store with the middleware
const textStore = createStore({
  initialState,
  middleware: [middleware]
});

function TextEditor() {
  const [state, setState] = useStoreState(textStore);
  
  const handleChange = (e) => {
    setState({ text: e.target.value }, 'UPDATE_TEXT');
  };
  
  const handleUndo = () => {
    controls.undo(newState => textStore.setState(newState));
  };
  
  const handleRedo = () => {
    controls.redo(newState => textStore.setState(newState));
  };
  
  return (
    <div>
      <textarea
        value={state.text}
        onChange={handleChange}
      />
      <button
        onClick={handleUndo}
        disabled={!controls.canUndo()}
      >
        Undo
      </button>
      <button
        onClick={handleRedo}
        disabled={!controls.canRedo()}
      >
        Redo
      </button>
    </div>
  );
}
```

## Advanced Usage

### Creating a Store Hook Factory

```tsx
import { createStoreHook } from '@okenneth/reactive-state';

// Create a store and a custom hook for it
const { store: todoStore, useStore: useTodoStore } = createStoreHook({
  initialState: {
    todos: [],
    filter: 'all'
  }
});

// Use in components
function TodoList() {
  const [state, setState] = useTodoStore();
  
  // Now you can use the state and setState directly
  // without having to import the store separately
}
```

### Custom Middleware

```tsx
import { Middleware } from '@okenneth/reactive-state';

// Create a custom middleware
const analyticsMiddleware: Middleware<any> = (nextState, prevState, action) => {
  if (action) {
    // Send action to analytics service
    sendToAnalytics({
      action,
      timestamp: new Date().toISOString()
    });
  }
  
  return nextState;
};

// Use the middleware
const storeWithAnalytics = createStore({
  initialState: { /* ... */ },
  middleware: [analyticsMiddleware]
});
```

## SSR Support

ReactiveState works seamlessly with server-side rendering:

```tsx
// On the server
const serverStore = createStore({ initialState: { /* ... */ } });

// Render with initial data
const html = renderToString(<App store={serverStore} />);

// Serialize state for client hydration
const initialData = serverStore.getState();

// On the client
const clientStore = createStore({ 
  initialState: window.__INITIAL_DATA__ || { /* fallback */ } 
});

// Hydrate the application
hydrate(<App store={clientStore} />, document.getElementById('root'));
```

## Performance Optimization

ReactiveState is designed for optimal performance:

1. **Fine-grained reactivity**: Components only re-render when their specific dependencies change.
2. **Shallow equality checks**: Prevents unnecessary renders when state references change but values don't.
3. **Memoized selectors**: The `useSelector` hook only triggers updates when the selected state changes.
4. **Batch updates**: Multiple state changes within the same event loop are batched to avoid cascading renders.

## API Reference

### Core

#### `createStore<T>(options: CreateStoreOptions<T>): Store<T>`

Creates a new store with the provided options.

#### `createDerivedStore<D, T>(dependencies, deriveFn): Store<T>`

Creates a derived store that depends on other stores.

### Hooks

#### `useStore<T>(store: Store<T>): T`

Subscribes to a store and returns its current state.

#### `useSelector<T, R>(store: Store<T>, selector: (state: T) => R, equalityFn?): R`

Subscribes to a specific part of a store's state.

#### `useSetState<T>(store: Store<T>): (updater, action?) => void`

Returns a memoized setState function for the store.

#### `useStoreState<T>(store: Store<T>): [T, (updater, action?) => void]`

Returns both the state and setState function.

#### `useSelectorState<T, R>(store, selector, equalityFn?): [R, (updater, action?) => void]`

Returns both the selected state and setState function.

### Middleware

#### `createLoggerMiddleware(options?): Middleware<T>`

Creates middleware that logs state changes.

#### `createPerformanceMiddleware(options?): Middleware<T>`

Creates middleware that tracks update performance.

#### `createThrottleMiddleware(options?): Middleware<T>`

Creates middleware that throttles updates.

#### `createValidationMiddleware(validator): Middleware<T>`

Creates middleware that validates state changes.

#### `createUndoRedoMiddleware(options?): { middleware, controls }`

Creates middleware and controls for undo/redo functionality.

## License

MIT