createState
🔵 React Approach
This API is recommended for React developers who want simpler state management without learning Vue APIs. If you're familiar with Vue and want fine-grained reactivity, see createStore instead.
Overview
createState provides a React-friendly way to manage state with:
- Plain JavaScript objects (no
ref()orreactive()) - Actions for mutations
- Built-in middleware (persist, DevTools, etc.)
Signature:
function createState<T>(setup: () => T, options?: StateOptions<T>): UseState<T>;Basic Usage
Simple Counter
import { createState } from "reactivity-store";
const useCounter = createState(() => ({ count: 0 }), {
withActions: (state) => ({
increment: () => state.count++,
decrement: () => state.count--,
reset: () => (state.count = 0),
}),
});
// In component
function Counter() {
const { count, increment, decrement } = useCounter();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
</div>
);
}Built-in Middleware
💾 Persistent State (withPersist)
Automatically save state to localStorage:
import { createState } from "reactivity-store";
const useSettings = createState(
() => ({
theme: "light",
language: "en",
}),
{
// Simple: just provide a key
withPersist: "app-settings",
withActions: (state) => ({
setTheme: (theme: string) => (state.theme = theme),
setLanguage: (lang: string) => (state.language = lang),
}),
}
);Advanced persist options:
const useSettings = createState(() => ({ theme: "light" }), {
withPersist: {
key: "settings",
getStorage: () => sessionStorage, // Use sessionStorage
stringify: (state) => JSON.stringify(state),
parse: (str) => JSON.parse(str),
merge: (fromCreator, fromStorage) => ({ ...fromCreator, ...fromStorage }),
},
});🎬 Actions (withActions)
Define how to mutate state:
const useTodos = createState(
() => ({
todos: [],
filter: "all",
}),
{
withActions: (state) => ({
addTodo: (text: string) => {
state.todos.push({
id: Date.now(),
text,
done: false,
});
},
toggleTodo: (id: number) => {
const todo = state.todos.find((t) => t.id === id);
if (todo) todo.done = !todo.done;
},
removeTodo: (id: number) => {
state.todos = state.todos.filter((t) => t.id !== id);
},
setFilter: (filter: string) => {
state.filter = filter;
},
}),
}
);
// In component
const { todos, addTodo, toggleTodo } = useTodos();🛠️ Redux DevTools (withNamespace)
Debug with Redux DevTools:
const useCounter = createState(() => ({ count: 0 }), {
withNamespace: "Counter", // Shows up in DevTools
withActions: (state) => ({
increment: () => state.count++, // Tracked in DevTools
}),
});⚡ Performance Options
Control how deeply state changes are tracked:
const useStore = createState(() => ({ nested: { count: 0 } }), {
// Track nested property changes (default: true)
withDeepSelector: true,
// Stable selector for performance (default: false)
withStableSelector: false,
withActions: (state) => ({
increment: () => state.nested.count++,
}),
});Deep Selector Explained:
// Component updates when nested.count changes
const { nested } = useStore((state) => ({
nested: state.nested,
}));
// nested.count++ triggers re-render ✅// Component ONLY updates when nested object ref changes
const { nested } = useStore((state) => ({
nested: state.nested,
}));
// nested.count++ does NOT trigger re-render ❌
// state.nested = {...} triggers re-render ✅Combining Middleware
You can use multiple middleware together:
const useSettings = createState(() => ({ theme: "light", fontSize: 16 }), {
withPersist: "settings",
withNamespace: "Settings",
withActions: (state) => ({
setTheme: (theme) => (state.theme = theme),
increaseFontSize: () => state.fontSize++,
}),
});import { withActions, withPersist, withNamespace } from "reactivity-store";
const useSettings = createState(
withActions(
withPersist(() => ({ theme: "light", fontSize: 16 }), { key: "settings" }),
{
generateActions: (state) => ({
setTheme: (theme) => (state.theme = theme),
increaseFontSize: () => state.fontSize++,
}),
}
),
{ withNamespace: "Settings" }
);Recommendation: Use the Options API for better readability!
Comparison with Other Solutions
import { createState } from "reactivity-store";
const useCounter = createState(() => ({ count: 0 }), {
withActions: (state) => ({
increment: () => state.count++,
}),
});
// In component
const { count, increment } = useCounter();import { createStore, ref } from "reactivity-store";
const useCounter = createStore(() => {
const count = ref(0);
const increment = () => count.value++;
return { count, increment };
});
// In component
const { count, increment } = useCounter();import { create } from "zustand";
const useCounter = create((set) => ({
count: 0,
increment: () =>
set((state) => ({
count: state.count + 1,
})),
}));
// In component
const { count, increment } = useCounter();Key Differences:
- RStore createState: Direct mutation in actions, built-in middleware
- RStore createStore: Vue APIs (
ref,reactive,computed) - Zustand: Immutable updates with
setfunction
⚡ Using Selectors (performance)
Pick only the state you need:
// Get everything
const { count, increment } = useCounter();
// Pick specific fields
const count = useCounter((state) => state.count);
// Pick multiple fields
const { count, increment } = useCounter((state) => ({
count: state.count,
increment: state.increment,
}));Available Middleware Options
| Option | Type | Description |
|---|---|---|
withActions | (state) => Actions | Define state mutations |
withPersist | string | PersistOptions | Auto-save to localStorage |
withNamespace | string | Redux DevTools integration |
withDeepSelector | boolean | Track nested changes (default: true) |
withStableSelector | boolean | Stable selector for performance |
Important Notes
State is Read-Only in Components
State is read-only outside of actions. Mutations must happen inside withActions:
// ❌ Wrong - mutating state in component
const { count } = useCounter();
count++; // Won't work!
// ✅ Correct - use actions
const { count, increment } = useCounter();
increment(); // Works!Escape Hatch
For advanced cases, you can access the reactive state directly:
useCounter.getReactiveState().count++; // Use sparingly!But this bypasses type safety and DevTools tracking. Prefer using actions!
Next Steps
- Learn about subscriptions for listening to state changes
- Explore useReactiveState for component-local state
- See more examples
- Try the Vue approach with createStore