Skip to main content

createStore

createStore is an advanced state management function in Igris that combines state and actions into a single hook. It provides a powerful way to manage complex state logic with associated actions, supporting features like state selection, persistence, and server-side rendering.

Basic Usage

Creating a Store

import { createStore } from "igris";

const useCounter = createStore({ count: 0 }, ({ set, get }) => ({
increment: () => set({ count: get().count + 1 }),
decrement: () => set({ count: get().count - 1 }),
}));

Using in Components

function Counter() {
const { count, increment, decrement } = useCounter();

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

API Reference

createStore<T, R>

Creates a store hook with state and actions.

Parameters

  • initialState: T - Initial state object (must be key-value pairs)
  • callback: (methods: StoreMethods<T>) => R - Function that returns action methods
  • config?: StoreConfig - Optional configuration object

Returns: StoreHook<T, R>

StoreHook<T, R> Interface

interface StoreHook<T, R> {
// Hook usage
<S = T & R>(): S;
<S = T & R>(selector: (state: T & R) => S): S;

// Utility methods
get: () => T;
getServerState: () => T;
actions: R;
subscribe: (listener: StateListener<T>) => () => void;
set: (updateAction: React.SetStateAction<T>) => void;
persist: PersistHandler<T> | null;
}

Store Methods

Inside Components

// Full state and actions
const store = useCounter();

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

// Select specific actions
const { increment } = useCounter((state) => ({
increment: state.increment,
}));

Outside Components

// Get current state
const state = useCounter.get();

// Access actions
const { increment } = useCounter.actions;

// Subscribe to changes
const unsubscribe = useCounter.subscribe((newState) => {
console.log("State changed:", newState);
});

Advanced Usage

Complex Store Example

interface TodoState {
items: { id: number; text: string; completed: boolean }[];
filter: "all" | "active" | "completed";
}
interface TodoActions {
addTodo: (text: string) => void;
toggleTodo: (id: number) => void;
setFilter: (filter: TodoState["filter"]) => void;
getFilteredTodos(): TodoState["items"];
}

const useTodoStore = createStore(
{
items: [],
filter: "all" as const,
} as TodoState,
({ set, get }) => ({
addTodo: (text: string) =>
set((state) => ({
...state,
items: [
...state.items,
{
id: Date.now(),
text,
completed: false,
},
],
})),

toggleTodo: (id: number) =>
set((state) => ({
...state,
items: state.items.map((item) =>
item.id === id ? { ...item, completed: !item.completed } : item
),
})),

setFilter: (filter: TodoState["filter"]) => set({ ...get(), filter }),

getFilteredTodos() {
const state = get();
switch (state.filter) {
case "active":
return state.items.filter((item) => !item.completed);
case "completed":
return state.items.filter((item) => item.completed);
default:
return state.items;
}
},
})
);

Using with TypeScript

interface AuthState {
user: User | null;
isLoading: boolean;
error: string | null;
}

interface AuthActions {
login: (credentials: Credentials) => Promise<void>;
logout: () => void;
clearError: () => void;
}

const useAuth = createStore<AuthState, AuthActions>(
{
user: null,
isLoading: false,
error: null,
},
({ set, get }) => ({
async login(credentials) {
set({ ...get(), isLoading: true, error: null });
try {
const user = await apiLogin(credentials);
set({ ...get(), user, isLoading: false });
} catch (error) {
set({ ...get(), error: error.message, isLoading: false });
}
},

logout() {
set({ ...get(), user: null });
},

clearError() {
set({ ...get(), error: null });
},
})
);

With Persistence

import { enablePersist } from "igris/persistence";

const usePersistedStore = createStore(
{ theme: "light", settings: {} },
({ set }) => ({
toggleTheme: () =>
set((state) => ({
...state,
theme: state.theme === "light" ? "dark" : "light",
})),
}),
{
name: "app-settings",
persist: enablePersist(),
}
);

Selective Re-rendering

function ThemeDisplay() {
// Only re-renders when theme changes
const theme = usePersistedStore((state) => state.theme);
return <div>Current theme: {theme}</div>;
}

function ThemeToggle() {
// Only re-renders when toggleTheme changes
const { toggleTheme } = usePersistedStore((state) => ({
toggleTheme: state.toggleTheme,
}));
return <button onClick={toggleTheme}>Toggle Theme</button>;
}