createStore
Create reactive stores with automatic TypeScript inference and built-in Immer support
API Reference
Syntax
const store = createStore(initialState);Parameters
initialState: Object defining your store's initial state and actions
Returns
A reactive store instance with:
value: Current state (read/write access with dependency tracking)setState(updater, options?): Update state with Immer supportsubscribe(options): Fine-grained subscription control
Examples
Simple State Management
import { createStore, useStore } from "zenbox";
// Types are automatically inferred
const counterStore = createStore({ count: 0 });
// Direct access state value via `value` property
console.log("Current count:", counterStore.value.count);
function Counter() {
  // Reactive subscription for components
  const { count } = useStore(counterStore);
  const increment = () => {
    counterStore.setState((state) => {
      state.count++; // Immer-powered mutations
    });
  };
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>+</button>
    </div>
  );
}Store with Actions
Co-locate state and behavior for clean, maintainable code:
const userStore = createStore({
  // State
  name: "Del Wang",
  blog: "https://del.wang",
  followers: 0,
  // Actions
  follow() {
    userStore.setState((state) => {
      state.followers++;
    });
  },
  async fetchProfile() {
    const response = await fetch("https://api.github.com/users/del-wang");
    const data = await response.json();
    userStore.setState(data);
  },
});
// Clean API calls
userStore.value.follow();
await userStore.value.fetchProfile();Update Patterns
1. Immer-style Updates (Recommended)
Perfect for complex mutations and nested state:
store.setState((state) => {
  state.count += 1;
  state.user.profile.name = "Updated";
  state.todos.push({ id: Date.now(), text: "New todo", completed: false });
});2. Partial Object Updates
Merge new properties with existing state:
const store = createStore({
  name: "John",
  age: 25,
  email: "john@example.com",
  avatar: "avatar.jpg",
});
// Shallow merge with existing state
store.setState({ name: "Jane", age: 30 });
// Result: { name: "Jane", age: 30, email: "john@example.com", avatar: "avatar.jpg" }
// Explicit overwrites (including null/undefined)
store.setState({ avatar: null });
// Result: { name: "Jane", age: 30, email: "john@example.com", avatar: null }
// Unknown properties are ignored
store.setState({ unknownField: "ignored" }); // No effect
// Result: { name: "Jane", age: 30, email: "john@example.com", avatar: null }3. Direct Assignment
For complete state replacement or when you prefer explicit assignment:
// Complete replacement
store.value = { count: 1, name: "Jane" };
// Partial updates (same as setState)
store.value = { count: 5 }; // Merges: { count: 5, name: "Jane" }4. Silent Updates
Update state without triggering subscriptions / re-renders:
// Background cache updates
store.setState({ cache: freshData }, { silent: true });Best Practices
✅ Co-locate state and actions
const store = createStore({
  count: 0,
  increment() {
    /* action logic */
  },
});✅ Use Immer-style for complex updates
store.setState((state) => {
  state.nested.property = newValue;
});✅ Handle async operations properly
async fetchData() {
  store.setState(state => { state.loading = true; });
  try {
    // API call
  } finally {
    store.setState(state => { state.loading = false; });
  }
}✅ Use silent updates for background data
store.setState({ cache: data }, { silent: true });Related APIs
createProvider- Component-scoped storesuseStore- Subscribe to store changesuseComputed- Derived reactive valuesuseWatch- Side effects on store changes