Skip to content

lurgi/mesa

Repository files navigation

Mesa

High-performance React state management with fine-grained reactivity

Mesa provides automatic dependency tracking and path-based subscriptions, ensuring only the components that need updates actually re-render. Zero dependencies, minimal bundle size, maximum performance.

⚡ Features

🎯 Fine-Grained Updates

Only re-render components that use changed data. Mesa automatically tracks dependencies and updates only what's necessary.

🚀 Simple API

Three core functions: proxy(), useStore(), and useInitSync(). No complex selectors, no manual optimizations.

🪶 Lightweight

~1KB gzipped with no external dependencies. Built for modern React applications.

Quick Start

Installation

npm install mesa-react

Basic Usage

import { proxy, useStore, useInitSync } from "mesa-react";

// Create a reactive store
const store = proxy({
  count: 0,
  user: {
    name: "John",
    age: 30,
  },
});

// Use in components
function Counter() {
  const count = useStore(store, (s) => s.count);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => store.count++}>Increment</button>
    </div>
  );
}

function UserInfo() {
  const userName = useStore(store, (s) => s.user.name);
  const userAge = useStore(store, (s) => s.user.age);

  return (
    <div>
      <p>Name: {userName}</p>
      <p>Age: {userAge}</p>
    </div>
  );
}

How It Works

Automatic Dependency Tracking

Mesa uses JavaScript Proxies to automatically track which properties your components access. When you use useStore(), Mesa creates a subscription to only the specific paths you read.

function MyComponent() {
  const userName = useStore(store, (s) => s.user.name);
  // Only subscribes to 'user.name' property

  return <div>{userName}</div>;
  // Will re-render only when user.name changes
}

Path-Based Subscriptions

Mesa tracks deep object access automatically:

function DeepComponent() {
  const theme = useStore(store, (s) => s.user.profile.settings.theme);
  // Only subscribes to this specific path

  return <div>{theme}</div>;
}

Immutable Updates

Mesa handles immutable updates automatically:

// These all work seamlessly
store.count = 5;
store.user.name = "Jane";
store.items.push(newItem);
store.nested.deep.value = "updated";

Advanced Patterns

Computed Values

const store = proxy({
  items: [],
  get totalCount() {
    return this.items.length;
  },
  get expensiveValue() {
    return this.items.reduce((sum, item) => sum + item.value, 0);
  },
});

function Summary() {
  const totalCount = useStore(store, (s) => s.totalCount);
  const expensiveValue = useStore(store, (s) => s.expensiveValue);

  return (
    <div>
      <p>Total Items: {totalCount}</p>
      <p>Total Value: {expensiveValue}</p>
    </div>
  );
}

Declarative Initialization with useInitSync

const store = proxy({
  data: null,
  loading: false,
  error: null,
});

function DataComponent() {
  // Declarative initialization - runs once per component tree
  useInitSync(store, async (state) => {
    state.loading = true;
    try {
      const response = await fetch("/api/data");
      state.data = await response.json();
    } catch (err) {
      state.error = err.message;
    } finally {
      state.loading = false;
    }
  });

  const data = useStore(store, (s) => s.data);
  const loading = useStore(store, (s) => s.loading);
  const error = useStore(store, (s) => s.error);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!data) return <div>No data</div>;

  return <div>{JSON.stringify(data)}</div>;
}

Local Component State

function LocalCounter() {
  const localStore = useMemo(() => proxy({ count: 0 }), []);
  const count = useStore(localStore, (s) => s.count);

  return <button onClick={() => localStore.count++}>Count: {count}</button>;
}

Performance Benefits

Fine-Grained Subscriptions

Mesa tracks only the specific paths your components access:

function Header() {
  const userName = useStore(store, (s) => s.user.name);
  // Only subscribes to 'user.name' path
  return <header>Welcome, {userName}!</header>;
}

function Sidebar() {
  const sidebarContent = useStore(store, (s) => s.sidebar.content);
  // Only subscribes to 'sidebar.content' path
  return <aside>{sidebarContent}</aside>;
}

function Main() {
  const content = useStore(store, (s) => s.content);
  // Only subscribes to 'content' path
  return <main>{content}</main>;
}

Optimized Re-renders

Only components that actually use changed data will re-render:

function CountComponent() {
  const count = useStore(store, (s) => s.count);
  // Only re-renders when count changes
  return <div>Count: {count}</div>;
}

function NameComponent() {
  const name = useStore(store, (s) => s.name);
  // Only re-renders when name changes
  return <div>Name: {name}</div>;
}

// When store.count changes, only CountComponent re-renders
// When store.name changes, only NameComponent re-renders

API Reference

proxy(initialState)

Creates a reactive store from an initial state object.

const store = proxy({
  count: 0,
  user: { name: "John" },
});

useStore(store, selector)

Hook to subscribe to store changes. Returns the selected value.

const count = useStore(store, (s) => s.count);
const userName = useStore(store, (s) => s.user.name);

useInitSync(store, initializer, options?)

Hook for declarative state initialization with atomic updates.

// Object initialization
useInitSync(store, {
  data: initialData,
  initialized: true,
});

// Function initialization
useInitSync(store, async (state) => {
  const data = await fetchData();
  state.data = data;
  state.loading = false;
});

// With dependency tracking
useInitSync(store, async (state) => {
  const userData = await fetchUser(userId);
  state.user = userData;
}, { deps: [userId] });

Migration Guide

From Redux

// Before (Redux)
const mapStateToProps = (state) => ({
  count: state.counter.count,
  user: state.user,
});

// After (Mesa)
const count = useStore(store, (s) => s.count);
const user = useStore(store, (s) => s.user);

From Zustand

// Before (Zustand)
const count = useStore((state) => state.count);
const increment = useStore((state) => state.increment);

// After (Mesa)
const count = useStore(store, (s) => s.count);
const increment = () => store.count++;

From Context

// Before (Context)
const { state, dispatch } = useContext(MyContext);

// After (Mesa)
const count = useStore(store, (s) => s.count);

License

MIT License - see LICENSE for details.


Built with ❤️ for the React community.

About

⚒️ High-performance React state management with fine-grained reactivity

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published