Skip to content

MrWangJustToDo/reactivity-store

Repository files navigation

🚀 RStore

Vue-inspired Reactive State Management for React

Deploy npm downloads

Bring Vue's reactivity system to React with zustand-like simplicity. Direct mutation, automatic UI updates - no manual subscriptions needed!

Documentation · Getting Started · Examples


Why RStore?

  • 🎯 Direct Mutation - No setState, just mutate and UI updates automatically
  • Vue Reactivity - Use ref(), reactive(), computed() from @vue/reactivity
  • 🪝 Zustand-like API - Clean, minimal API design
  • 🔌 Built-in Middleware - Persist, actions, Redux DevTools out of the box
  • 📘 TypeScript First - Full type safety and excellent IntelliSense
  • 🚀 Zero Boilerplate - No reducers, no dispatch, no manual subscriptions

Installation

npm install reactivity-store
# or
pnpm add reactivity-store

Quick Start

🟢 Vue Approach

For Vue developers or those wanting fine-grained reactivity:

import { createStore, ref, computed } from "reactivity-store";

const useCounter = createStore(() => {
  const count = ref(0);
  const doubled = computed(() => count.value * 2);

  const increment = () => count.value++; // Direct mutation!

  return { count, doubled, increment };
});

function App() {
  const { count, doubled, increment } = useCounter();
  return (
    <div>
      <p>Count: {count}</p>
      <p>Doubled: {doubled}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
}

🔵 React Approach

For React developers who want simplicity without learning Vue APIs:

import { createState } from "reactivity-store";

const useCounter = createState(
  () => ({ count: 0 }),
  {
    withActions: (state) => ({
      increment: () => state.count++,
      decrement: () => state.count--
    })
  }
);

function App() {
  const { count, increment, decrement } = useCounter();
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>+1</button>
      <button onClick={decrement}>-1</button>
    </div>
  );
}

Two Approaches, One Library

🟢 Vue Approach 🔵 React Approach
Best for Vue developers React developers
APIs ref, reactive, computed Plain objects + actions
Features Auto-computed, lifecycle hooks Middleware: persist, DevTools
Use createStore createState

Built-in Middleware

💾 Persistent State

const useSettings = createState(
  () => ({ theme: "light", language: "en" }),
  {
    withPersist: "app-settings", // Auto-saves to localStorage
    withActions: (state) => ({
      setTheme: (theme) => state.theme = theme
    })
  }
);

🛠️ Redux DevTools

const useCounter = createState(
  () => ({ count: 0 }),
  {
    withNamespace: "Counter", // Shows up in Redux DevTools
    withActions: (state) => ({
      increment: () => state.count++
    })
  }
);

⚡ Performance Options

const useStore = createState(
  () => ({ nested: { count: 0 } }),
  {
    withDeepSelector: true,      // Track nested changes (default: true)
    withStableSelector: false,   // Stable selector for performance
    withActions: (state) => ({
      increment: () => state.nested.count++
    })
  }
);

Advanced Features

Lifecycle Hooks

import { createStoreWithComponent, ref, onMounted, onUnmounted } from "reactivity-store";

const Timer = createStoreWithComponent({
  setup: () => {
    const seconds = ref(0);
    let timer;

    onMounted(() => {
      timer = setInterval(() => seconds.value++, 1000);
    });

    onUnmounted(() => {
      clearInterval(timer);
    });

    return { seconds };
  }
});

function App() {
  return <Timer>{({ seconds }) => <div>{seconds}s</div>}</Timer>;
}

Component-local Reactive State

import { useReactiveState } from "reactivity-store";

function TodoList() {
  const [state, setState] = useReactiveState({
    todos: [],
    filter: "all"
  });

  const addTodo = (text) => {
    setState((s) => {
      s.todos.push({ id: Date.now(), text, done: false });
    });
  };

  return <div>{/* ... */}</div>;
}

State Subscriptions

const useCounter = createState(
  () => ({ count: 0 }),
  { withActions: (s) => ({ increment: () => s.count++ }) }
);

// Subscribe anywhere in your app
useCounter.subscribe(
  (state) => state.count,
  (count) => console.log("Count changed:", count)
);

Comparison

Traditional React

const [count, setCount] =
  useState(0);

setCount(prev => prev + 1);

RStore (Vue Approach)

const count = ref(0);

count.value++;

RStore (React Approach)

const useCount = createState(
  () => ({ count: 0 }),
  { withActions: (s) => ({
    increment: () => s.count++
  })}
);

API Overview

API Purpose
createStore Vue-style reactive stores with ref(), reactive(), computed()
createState React-style state with actions and middleware
createStoreWithComponent Component-scoped stores with lifecycle hooks
useReactiveState Component-local reactive state (like useState but reactive)
useReactiveEffect Side effects with automatic dependency tracking

Documentation

Visit https://mrwangjusttodo.github.io/r-store/ for complete documentation:

License

MIT © MrWangJustToDo

About

A React state-management, like zustand, power by @vue/reactivity

Resources

License

Stars

Watchers

Forks

Packages

No packages published