Skip to content

createStore

🟢 Vue Approach

This API is recommended for Vue developers or those familiar with Vue's reactivity system. If you're a React developer looking for simpler patterns, see createState instead.

Overview

createStore allows you to use Vue's reactivity APIs (ref, reactive, computed) directly in React. It feels just like writing Vue Composition API!

Signature:

ts
function createStore<T>(setup: () => T): UseStore<T>

Basic Usage

Using ref()

The simplest way to create reactive state:

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

const useCounter = createStore(() => {
  const count = ref(0);

  // Direct mutation - no setState needed!
  const increment = () => count.value++;
  const decrement = () => count.value--;

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

// In component - ref auto-unwraps
function Counter() {
  const { count, increment } = useCounter();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
}

Auto Unwrapping

ref values are automatically unwrapped when returned from the store, so you can use count directly instead of count.value in components!

Using reactive()

For objects, use reactive():

tsx
import { createStore, reactive } from "reactivity-store";

const useUser = createStore(() => {
  const user = reactive({
    name: "",
    email: "",
    profile: {
      bio: "",
      avatar: ""
    }
  });

  // Mutate nested properties directly!
  const updateName = (name: string) => {
    user.name = name;
  };

  const updateBio = (bio: string) => {
    user.profile.bio = bio;
  };

  return { user, updateName, updateBio };
});

Using computed()

Computed values automatically track dependencies:

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

const useCart = createStore(() => {
  const items = ref([
    { id: 1, name: "Apple", price: 1.5, quantity: 2 },
    { id: 2, name: "Banana", price: 0.8, quantity: 3 }
  ]);

  // Automatically recalculates when items change
  const total = computed(() =>
    items.value.reduce((sum, item) =>
      sum + item.price * item.quantity, 0
    )
  );

  const addItem = (item) => {
    items.value.push(item);
  };

  return { items, total, addItem };
});

⚡ Using Selectors (performance)

Pick only the state you need for better performance:

tsx
// Get everything
const { count, increment } = useCounter();

// Pick specific fields
const count = useCounter(state => state.count);

// Pick multiple fields
const { count, doubled } = useCounter(state => ({
  count: state.count,
  doubled: state.doubled
}));

Comparison with Other Solutions

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

const useCount = createStore(() => {
  const count = ref(0);
  const increment = () => count.value++;

  return { count, increment };
});

// In component
const { count, increment } = useCount();
tsx
import { createState } from "reactivity-store";

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

// In component
const { count, increment } = useCount();
tsx
import { create } from "zustand";

const useCount = create((set) => ({
  count: 0,
  increment: () => set((state) => ({
    count: state.count + 1
  }))
}));

// In component
const { count, increment } = useCount();

Key Differences:

  • RStore createStore: Direct mutation with Vue APIs (ref, reactive, computed)
  • RStore createState: Actions-based with middleware support
  • Zustand: Immutable updates with set function

Available Vue APIs

All Vue reactivity APIs are available:

APIDescriptionExample
ref()Reactive primitive valueconst count = ref(0)
reactive()Reactive objectconst user = reactive({ name: '' })
computed()Derived stateconst doubled = computed(() => count.value * 2)
watch()Side effectswatch(() => count.value, (val) => console.log(val))
watchEffect()Auto-tracking side effectswatchEffect(() => console.log(count.value))

See @vue/reactivity docs for complete API reference.

Important Rules

State is Read-Only in Components

State returned from the selector is read-only. You must define mutation functions inside createStore:

tsx
// ❌ Wrong - mutating state in component
const { count } = useCount();
count.value++; // Won't work!

// ✅ Correct - use mutation functions
const { count, increment } = useCount();
increment(); // Works!

Ref Auto-Unwrapping

When you return ref() values from the store, they are automatically unwrapped in components:

tsx
const useStore = createStore(() => {
  const count = ref(0);
  return { count }; // count.value inside
});

// In component
const { count } = useStore();
console.log(count); // No .value needed!

Next Steps

Released under the MIT License.