npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mocovi

v0.18.7

Published

Data Store core, with no boilerplate

Readme

Mocovi State Management Library

Mocovi is a React state management library designed for managing collections of typed data models. It provides reactive hooks with optimized re-rendering, flexible filtering and sorting capabilities, and built-in support for persistence and synchronization.

Key Features

  • Type-safe collections of models with required id field
  • Reactive hooks with optimized change detection and selective re-rendering
  • Flexible data access with filtering, sorting, and regex support
  • Non-reactive access for background operations without triggering re-renders
  • Pluggable persistence with synchronous storage backends
  • Configurable synchronization with backend servers

Quick Start

import { createCollection, useStore } from 'mocovi';

// Define your model type
interface User {
  id: string;
  name: string;
  email: string;
  role: 'admin' | 'user';
}

// Create a collection (do this once, typically in a separate file)
createCollection<User>("users", [
  { id: "1", name: "John Doe", email: "[email protected]", role: "admin" },
  { id: "2", name: "Jane Smith", email: "[email protected]", role: "user" }
]);

// Use the collection in your components
function UserList() {
  const { collection, setModel } = useStore<User>("users");
  
  return (
    <div>
      {collection.map(user => (
        <div key={user.id}>{user.name} - {user.email}</div>
      ))}
    </div>
  );
}

Core Concepts

Model

All data in Mocovi must implement the Model interface with a required id field:

interface Model {
  id: string;
  created_at?: number;
  updated_at?: number; 
  synced_at?: number;
  deleted_at?: number;
  changed_at?: number;
}

Store

A Store<Data> manages a collection of models, providing access to controllers, hooks, and configuration for persistence and synchronization.

BaseController

The BaseController<Data> provides core methods for data manipulation:

  • get(id) - retrieve a model by ID
  • getCollection() - get all models as array
  • set(model) - update or insert a model
  • setCollection(models) - replace entire collection
  • setField(id, field, value) - update specific field

Primary Hook: useStore

The useStore hook is the recommended way to access and manipulate state data. It provides a unified interface that replaces the legacy hooks.

Collection Access

const { collection, setCollection, setModel, controller } = useStore<User>("users");

Returns the entire collection and functions to update it. Component re-renders when any model changes.

Filtered Access by ID

const { collection, model, setModel } = useStore<User>("users", "user123");

Returns array with 0-1 models matching the ID. model is collection[0] || null for convenience.

Filtered Access by Criteria

// Filter by multiple fields
const { collection } = useStore<User>("users", { 
  role: "admin", 
  active: true 
});

// Filter with regex
const { collection } = useStore<User>("users", { 
  name: /^John/ 
});

Returns models matching all specified criteria.

Sorted Collections

// Sort by field
const { collection } = useStore<User>("users", undefined, "name");

// Filter and sort
const { collection } = useStore<User>("users", { role: "admin" }, "name");

Returns filtered and sorted collection.

Server Communication

const { useCom } = useStore<User>("users");
const com = useCom();

// Send custom commands
com.send("customCommand", { data: "example" });

Non-Reactive Access: useController

Use useController when you need data access without triggering component re-renders:

import { useController } from 'mocovi';

function BackgroundSync() {
  const { controller, useCom } = useController<User>("users");
  
  useEffect(() => {
    const handleChange = (data: User[]) => {
      console.log('Data changed but component won\'t re-render');
    };
    
    controller.subscribe(handleChange);
    return () => controller.unsubscribe(handleChange);
  }, []);
  
  const syncData = () => {
    // Direct data manipulation without re-renders
    controller.set({ id: "new", name: "New User", email: "[email protected]", role: "user" });
  };
  
  return <button onClick={syncData}>Sync Data</button>;
}

Collection Creation and Configuration

Basic Creation

import { createCollection } from 'mocovi';

createCollection<User>("users", initialData);

With Persistence

createCollection<User>("users", initialData, {
  persist: {
    get: (key) => localStorage.getItem(key),
    set: (key, value) => localStorage.setItem(key, value)
  }
});

With Synchronization

createCollection<User>("users", initialData, {
  sync: "auto", // "auto" | "get" | "set" | "manual" | false
});

With Custom Controller

interface UserController {
  promoteToAdmin: (userId: string) => void;
}

createCollection<User, UserController>("users", initialData, {
  createController: (baseController) => ({
    promoteToAdmin: (userId: string) => {
      const user = baseController.get(userId);
      if (user) {
        baseController.set({ ...user, role: "admin" });
      }
    }
  })
});

Legacy Hooks (now removed from the library)

The following hooks are maintained for backward compatibility but should be migrated to useStore:

  • useCollection()useStore("storeId")
  • useModel("modelId")useStore("storeId", "modelId")
  • useSelected() → custom filtering with useStore
  • Legacy useController()useStore for most cases, or new useController("storeId") for non-reactive access

Persistence and Synchronization

Persistence

Mocovi supports pluggable synchronous storage:

const persist = {
  get: (key: string) => localStorage.getItem(key) || undefined,
  set: (key: string, value: string) => localStorage.setItem(key, value)
};

Synchronization Modes

  • "auto" - Full bidirectional sync (send and receive changes)
  • "get" - Only receive changes from server
  • "set" - Only send changes to server
  • "manual" - Use controller.fetch() and manual sync
  • false - No synchronization

Server Communication

const { useCom } = useStore<User>("users");
const com = useCom((message) => {
  console.log('Received message:', message);
});

// Send commands
com.send("fetchUsers");
com.send("updateUser", { id: "123", name: "Updated Name" });

Debugging Components

Mocovi provides debugging components for development:

// Debug UI components have been removed from the core package. See separate debug library for store inspection.

Utility Functions

import { clearAll, printDiff } from 'mocovi';

// Clear all stores (useful for logout)
clearAll();

// Debug object differences
printDiff("user", oldUser, newUser, changes);

Migration Guide

From Legacy Hooks

// OLD: Legacy hooks
const [collection, setCollection] = useCollection();
const [model, setModel] = useModel("user123");
const [selected, setSelected] = useSelected();

// NEW: Unified useStore
const { collection, setCollection } = useStore<User>("users");
const { model, setModel } = useStore<User>("users", "user123"); 
// For selected, implement custom selection logic with filtering

From Direct Controller Access

// OLD: Direct controller with re-renders
const controller = useController();

// NEW: Non-reactive controller access
const { controller } = useController<User>("users");

Best Practices

  1. Use useStore for UI components that need to re-render on data changes
  2. Use useController for background operations like data syncing, logging, or side effects
  3. Create collections once during app initialization, not in render functions
  4. Use TypeScript for better type safety and developer experience
  5. Leverage filtering and sorting to minimize component re-renders
  6. Implement proper cleanup when using controller subscriptions in useEffect

Performance Considerations

  • Components only re-render when filtered data actually changes
  • Use specific filters to minimize unnecessary re-renders
  • Consider useController for operations that don't need UI updates
  • Batched updates prevent excessive re-rendering during bulk operations

Future Roadmap

  • Support for async storage backends
  • Enhanced undo/redo functionality
  • Context-based store management with <Mocovi> provider
  • Improved synchronization conflict resolution
  • Performance optimizations for large datasets