Build a state module
As shared state grows, repeating its key and update rules across components becomes hard to maintain.
createReStateMethods keeps those details in one module and generates the hooks and helpers your application needs.
Define the state and its actions
This cart module owns the cart key, its initial value, and every allowed update:
import { createReStateMethods } from '@raulpesilva/re-state';
export type CartItem = {
id: string;
name: string;
price: number;
quantity: number;
};
type CartState = {
items: CartItem[];
};
const initialCart: CartState = {
items: [],
};
export const { useCart, useCartSelect, dispatchCart, getCart, resetCart } = createReStateMethods('cart', initialCart);
export const addItem = (item: Omit<CartItem, 'quantity'>): void => {
dispatchCart((currentCart) => {
const existingItem = currentCart.items.find((currentItem) => currentItem.id === item.id);
const items = existingItem
? currentCart.items.map((currentItem) =>
currentItem.id === item.id ? { ...currentItem, quantity: currentItem.quantity + 1 } : currentItem
)
: [...currentCart.items, { ...item, quantity: 1 }];
return { items };
});
};
export const removeItem = (id: string): void => {
dispatchCart((currentCart) => ({
items: currentCart.items.filter((item) => item.id !== id),
}));
};
export const clearCart = (): void => {
resetCart();
};Each action receives the current cart and returns a new value. Components do not need to know the state key or repeat the update rules.
Use the module from a component
A component that only reads the cart can use the generated read-only hook:
import { addItem, clearCart, removeItem, useCartSelect } from '../states/cart';
export function Cart() {
const cart = useCartSelect();
const total = cart.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
return (
<section>
<h2>Cart ({cart.items.length} items)</h2>
{cart.items.map((item) => (
<div key={item.id}>
<span>
{item.name} × {item.quantity}
</span>
<button type="button" onClick={() => removeItem(item.id)}>
Remove
</button>
</div>
))}
<p>Total: {total.toFixed(2)}</p>
<button type="button" onClick={() => addItem({ id: '1', name: 'Product', price: 10 })}>
Add product
</button>
<button type="button" onClick={clearCart}>
Clear cart
</button>
</section>
);
}The total is derived from the items instead of stored separately, so it cannot drift out of sync with the cart.
What the factory creates
For createReStateMethods('cart', initialCart), the returned object contains:
| Method | Use it to |
|---|---|
useCart() | Read and update the cart in a component |
useCartSelect() | Read the cart in a component without a setter |
dispatchCart(value) | Update the cart from any code |
getCart() | Read the latest cart without subscribing |
resetCart() | Restore initialCart |
Keep the factory call at module scope so the state has one clear owner. Prefer the read-only hook in display components and expose actions for domain updates.
If you need custom method names or only one helper, use the
individual factories. For persistence or another side effect outside React, subscribe
with onReStateChange.
Organize by feature
Start with one state module per feature:
src/
states/
cart.ts
session.ts
theme.ts
components/
Cart.tsxYou can split a module later if it becomes too large. The important part is that components depend on its public hooks and actions, not on duplicated keys or update logic.